Skill 01

Networking

Networking is how computers and devices connect and route data to each other, and how you set up and secure a network and its servers.

01 Why now
In 1971, in the magazine Radical Software, Paul Ryan saw where the fight was headed and named it cybernetic guerrilla warfare. The ground was no longer only terrain and the gun; it was the information environment, the flow of messages and data and the tools that carry them. "All power does not proceed from the end of a gun," he wrote, and "the most important data banks are in our brain cells." His instruction was plain: own your tools, own your information, and keep both decentralized. What Ryan could only sketch then now fits in your hand. A full server runs on a board that costs less than a phone, so the chat, the DNS, and the private access a small group relies on can all run on hardware you hold and control. That is cybernetic guerrilla warfare made literal: a node in a rack that answers to you, and to no one else.
02 Case files
From Sun Tzu to Mao, guerrilla doctrine favored small, dispersed forces over large, fixed ones, and the same logic now governs the network. In 1990, Jean-Max Noyer named the modern form the techno-guérilla, the point at which the technical dimension becomes the center of a conflict rather than an accessory to it. Recent history makes the same point. In 2018 the FBI built and ran its own encrypted phone network, ANOM. Twelve thousand people trusted it, and when the trap sprang in 2021, eight hundred were arrested. The encryption never broke. The users had trusted servers they did not own. As the LFM essay Encryption Is Not Trust shows, encryption protects the channel but does not protect the network. A self-hosted node is how a small group refuses that trade.
03 Overview
You start with a box of parts and finish with a working, secured server your team uses every day. First the basics, so you know what you are doing: what a network is, how a machine gets an address and is reached by name, what SSH, ports, a firewall, and a VPN are. Then the build, in order: assemble it, get it online, secure it, put it on its own network, and run a chat server only your team can reach.
On this page
01

What you are building

A small team needs to talk without handing its messages to a company's servers. The kit is three pieces that trust each other and nothing else: a phone (a GrapheneOS phone running the Element chat app), a laptop you run everything from, and a server the team connects to. You are building the server.

The server is a Raspberry Pi 5, a full computer the size of a deck of cards, kept on all the time. It runs the team's chat and filters ad and tracker traffic for every device. Your people reach it over a private connection from anywhere, with nothing left open to the public internet. The phone and the laptop are clients; the Pi is the server they talk to.

Check on learning
In this kit, which device is the server?
The phone and the laptop are best described as:
02

Networks & the internet

A network is a set of devices connected so they can pass data to each other. The devices behind your own router, your laptop, your phone, and the Pi, are your local network, or LAN. Everything outside your router is the internet. The router sits between the two: it is the gateway out, and the boundary that keeps the inside in.

The internet
everything outside
Your router
gateway / boundary
Laptop
client
Phone
client · Element
Your Pi
server

When your laptop talks to the Pi, both are on your LAN, so the traffic goes across your own network and never leaves the house. When your laptop loads a website, the traffic goes out through the router to the internet and back. Keeping the server's traffic on the inside, off the public internet, is most of what "securing it" means.

Check on learning
Your laptop sends a file to the Pi, both on your home network. Does it go out to the internet?
What sits between your network and the internet?
03

Addresses & names

Every device on a network has an IP address, a number that says where it is, like 192.168.8.50. Addresses that begin 192.168 (and 10., and 172.16 through 172.31) are private: they work only inside your network. The public internet has no route to a private address, so nothing on the outside can reach your Pi directly by it. The one public address belongs to your router, which speaks to the internet for everything behind it.

People remember names, not numbers, so DNS is the lookup that turns a name into an address. On your own network the Pi answers to the name node.local, so you reach it by name instead of memorizing its number.

Check on learning
Your Pi has the address 192.168.8.50. Can a stranger on the internet connect to it directly?
What does DNS do?
04

Reaching it securely

SSH (Secure Shell) is the encrypted way you operate the server from your laptop's command line. Instead of a password it uses a key pair: a private key that never leaves your laptop, and a public key that sits on the Pi. The two are matched at login, and a key cannot be guessed the way a password can.

A server runs many services at once, so each one listens on a numbered port (SSH listens on port 22). A firewall decides which ports anything outside can reach. Set to default-deny, every port is closed until you open it; you open only what you need, and only to the people you trust.

To let your team reach the server from another city without opening it to the public internet, you put all of your devices on a private VPN, a tunnel only your machines can use. The server is reachable over that tunnel and nowhere else, so there is nothing on the open internet for anyone to find. The limit is worth stating: a VPN controls what is reachable, not who is trusted. The machine that coordinates the tunnel still sits on the internet and has to be hardened too, and any device you add is inside the tunnel with the rest.

Check on learning
In key-based SSH, where does your private key stay?
The firewall is default-deny. A service runs but you never opened its port. Can the outside reach it?
05

Identify your hardware

The server is one Raspberry Pi 5. An NVMe SSD on an M.2 HAT is its disk, a microSD card is the install medium you start from, and a 27W USB-C supply powers it. A GL.iNet travel router is the network, and you set everything up from a laptop.

Hardware to get
GL.iNet Slate AX, AXT1800 the travel routerSpecific
Laptop Linux-compatible, modernAny brand
microSD card, 32GB or larger the installerAny brand
Mini rack, 10 inch desktop holds the Pi and the routerAny brand
NVMe HAT and SSD the Pi's disk, replaces the cardAny brand
Raspberry Pi 5, 8GB the serverSpecific
USB-C power supply, 27W the official Pi 5 supplyAny brand
VPS, small runs your own Headscale, the VPN coordinatorAny host
Software, all free and open source
Raspberry Pi OS Lite + Imager the system, and the tool that writes itFree
Docker and Docker Compose runs the services in containersFree
Synapse the Matrix chat serverFree
Element the Matrix chat clientFree
AdGuard Home network-wide DNS filteringFree
Headscale + WireGuard your private VPNFree

The laptop is the one piece you reuse across every skill; everything else here is specific to this build. Click each port and chip to see what it does in the build.

Check on learning
Once built, which part does the server run from day to day?
What is the microSD card for?
06

Open your terminal

A terminal is a window where you type commands instead of clicking, and the program that reads them is the shell. Every command later in this module runs on the Pi, which is Linux, so once you connect the commands are identical on any laptop. Only opening the terminal and connecting differ.

macOS: open Terminal from Applications, then Utilities (shell is zsh; the SSH client is already installed). Linux: open your terminal app, often Ctrl+Alt+T (shell is usually bash; the SSH client is installed). Windows: open Windows Terminal or PowerShell; Windows 10 and 11 ship the same OpenSSH client, so the connection commands match. You do not need the old PuTTY program.

Run this on any OS; it prints the client version:

$ ssh -V
Output · draft, confirm on the Pi
OpenSSH_9.7p1, OpenSSL 3.0.13 30 Jan 2024
# macOS prints a LibreSSL build; Windows prints the same OpenSSH line. exact versions to be captured on the bench.
Check on learning
You run a command on the Pi from a Windows laptop; a teammate runs it from a Mac. Do the commands differ?
On Windows, which SSH client do you use?
07

Install the imager

Raspberry Pi Imager is the official program that writes the operating system to the microSD card. It runs on macOS, Windows, and Linux. Download it from the Raspberry Pi site and install it.

Version matters Use a current Raspberry Pi Imager (version 2 or newer). Current Raspberry Pi OS configures its first boot with cloud-init, and older Imager versions cannot write it, so they silently ignore your settings. Check Help, About if you are unsure, and update it if it is old.
Check on learning
What does Raspberry Pi Imager do?
Why use a current Raspberry Pi Imager?
08

Flash the OS

Choose Device: Raspberry Pi 5. Choose OS: Raspberry Pi OS Lite (64-bit), the server build with no desktop. Choose Storage: the microSD card.

Click Next, then Edit Settings. Set the hostname to node. Set a username and password (there is no default pi/raspberry account anymore). Set your wifi name and password, and set Wireless LAN country (without it the wifi will not associate). Set your locale and keyboard. On the Services tab, enable SSH and choose public-key authentication, pasting your laptop's public key (you make that key in Unit 10; if you have one already, paste ~/.ssh/id_ed25519.pub, never the private file). Then write the card.

Imager settings · screenshots to be captured
# step-by-step screenshots of the Device / OS / Storage / Edit Settings screens go here, captured during the build.
If the Pi boots into a setup wizard and ignores your settings That points to an Imager too old to write the current first-boot format. Update to a current Imager (version 2 or newer) and re-flash the card.
Check on learning
Why write Raspberry Pi OS Lite for a server with no monitor?
Why set SSH and wifi in Edit Settings before writing the card?
09

Assemble & power on

Fit the M.2 HAT and seat the NVMe SSD in it. Insert the microSD card you flashed in Unit 08. Connect an Ethernet cable from the Pi to the GL.iNet router. Connect the 27W USB-C supply last. The Pi 5 boots on its own the moment power reaches it; you do not press anything to start it. It reads the card and configures itself. The small onboard button does a soft shutdown, and powers it back on after one.

Headless Headless means no monitor or keyboard on the Pi. It boots on its own, joins the network through the Ethernet cable, and you reach it from the laptop in Unit 10.
Check on learning
You connect power to a fresh Pi 5. How does it start?
What does headless mean here?
10

Reach it over SSH

Recall from the basics: the router gave the Pi a private address like 192.168.8.50, and it answers to the name node.local, so you reach it by name. SSH uses a key pair: the public key is on the Pi (you put it there when you flashed the card), the private key never leaves your laptop. If you do not have a key yet, make one on the laptop:

$ ssh-keygen -t ed25519 -C "laptop-to-node"

Set a passphrase when it asks; that encrypts the private key on disk, so a copied key file is useless without it.

Output · draft, confirm on the Pi
The authenticity of host 'node.local (192.168.8.50)' can't be established.
ED25519 key fingerprint is SHA256:nThbg6kXU...redacted.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'node.local' (ED25519) to the list of known hosts.
Linux node 6.12.x-rpi ...
# the first connection asks once to trust the Pi's key; after that it is silent. to be captured on the bench.
node $ whoami    # your user
node $ hostname  # node

Before changing anything, update the system. Use full-upgrade, not plain upgrade, so firmware and kernel updates can pull in what they need.

node $ sudo apt update && sudo apt full-upgrade -y

Work through the rehearsal below. You connect, bring the Pi current, and set the firewall, typing each real command yourself. It walks the common mistakes, including the firewall step that locks most people out of a remote machine, so you make them here instead of on the Pi. This rehearsal carries through Unit 12, hardening.

If it will not connect Connection refused usually means the Pi has not finished its first boot; wait a minute and retry. Could not resolve hostname node.local means your network is not resolving .local names; find the Pi's address in the router's list of connected devices and use that instead. Permission denied (publickey) means the wrong key is being offered; point at the right one with ssh -i ~/.ssh/id_ed25519 [email protected]. REMOTE HOST IDENTIFICATION HAS CHANGED after a re-flash is fixed with ssh-keygen -R node.local.
Check on learning
How do you reach the Pi without memorizing its IP address?
After connecting, what do you do before changing anything?
11

Just-enough Linux

Move around: pwd prints where you are, ls lists what is here, cd changes directory. Administer: sudo runs one command as the administrator (root) and asks for your password. Install: apt is the package manager.

node $ sudo apt install -y git
Output · draft, confirm on the Pi
Reading package lists... Done
Building dependency tree... Done
The following NEW packages will be installed: git
Setting up git (1:2.x) ...
# a short real session (pwd, ls, cd, the install) to be captured on the bench.
Common stumbles Permission denied editing a system file means you forgot sudo. command not found means the tool is not installed yet. Unable to locate package means your package list is stale; run sudo apt update first.
Check on learning
Which line installs the program git on the Pi?
What does sudo do?
12

Harden the Pi

Recall from the basics: services listen on numbered ports, and a default-deny firewall closes every port you have not explicitly opened, so a service is never exposed just because it is running.

Put the hardening in a drop-in file so it is not overwritten. Confirm key login works in a second terminal before you do this, and keep your current session open while you test.

node $ sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
node $ sudo sshd -t && sudo systemctl restart ssh   # test config, then apply
node $ sudo apt install -y ufw
node $ sudo ufw default deny incoming
node $ sudo ufw allow from 192.168.8.0/24 to any port 22   # allow SSH from your LAN, BEFORE enable
node $ sudo ufw enable
Output · draft, confirm on the Pi
node $ sudo ufw status verbose
Status: active
Default: deny (incoming), allow (outgoing)
To           Action      From
--           ------      ----
22/tcp       ALLOW IN    192.168.8.0/24
# SSH is open only to your LAN; everything else is denied. to be captured on the bench.
You already practiced this The firewall steps above are the back half of the rehearsal in Unit 10: you set default-deny, watched a too-early ufw enable lock you out, recovered, then allowed SSH only from your LAN before enabling. Run that part again until it is automatic.
The two ways this bites people If PasswordAuthentication no seems ignored, a cloud-init drop-in in /etc/ssh/sshd_config.d/ is overriding it; check the effective value with sudo sshd -T | grep -i passwordauth. If you run ufw enable before you allow SSH, your session is cut and you need console access to recover. Allow SSH first, every time.
Full-disk encryption (advanced, optional) LUKS encrypts the disk so it is unreadable if the SSD is removed. On a headless Pi it is genuinely involved (a software cipher, and unlocking at boot over SSH), so it is an optional advanced step, not part of this base build.
Check on learning
You want SSH reachable from your laptop but not the internet. Which rule does that?
What happens if you run ufw enable before allowing SSH?
13

Move to NVMe

The official M.2 HAT+ enables the PCIe lane for you; a third-party HAT may need dtparam=pciex1 added to /boot/firmware/config.txt. Set the Pi to try the SSD first with sudo raspi-config (Advanced Options, Bootloader Version Latest, then Boot Order, NVMe/USB). Then clone the running card to the SSD with the maintained tool:

node $ git clone https://github.com/geerlingguy/rpi-clone
node $ cd rpi-clone && sudo cp rpi-clone rpi-clone-setup /usr/local/sbin
node $ lsblk            # the NVMe shows up as nvme0n1
node $ sudo rpi-clone nvme0n1
Output · draft, confirm on the Pi
node $ lsblk
NAME        MAJ:MIN  SIZE TYPE MOUNTPOINTS
mmcblk0     ...    [card]  disk        # the booted card, with its boot + root partitions
nvme0n1     ...     [SSD]  disk        # the empty target, before cloning
# exact sizes, partition rows, and mountpoints to be captured on the bench. rpi-clone then copies the hardened card to the SSD and fixes the PARTUUIDs.

Power off, remove the microSD, power back on, SSH in, then:

node $ findmnt /   # SOURCE should be /dev/nvme0n1p2, not mmcblk0
If it hangs at boot, or both disks look identical That is the classic clone failure: the card and the SSD share a partition ID (PARTUUID) so the bootloader cannot pick one. It happens when you clone with dd or the old unmaintained rpi-clone. Re-clone with the geerlingguy fork used above. If lsblk shows no nvme0n1 at all, the HAT ribbon cable is loose or backwards. If the SSD drops out at random, drop it to PCIe Gen 2 (remove any pciex1_gen=3 line) and use the official 27W supply.
Check on learning
Why run the server from the NVMe SSD instead of the card?
Why harden the Pi before cloning to the SSD?
14

Set up the router

Recall from the basics: DNS turns names into addresses. Run the resolver yourself and you control the lookups, refusing known ad and tracker domains for every device on the network at once.

Log in to the router (commonly 192.168.8.1). Change the admin password, set your wifi name and a strong passphrase, and add a guest network for devices you do not trust. Then turn on AdGuard Home (built into GL.iNet): enable it, and enable "AdGuard Home Handle Client Requests" so every device resolves through it. Set the upstream resolver inside AdGuard's own settings, not the router's basic DNS field.

Remote reach uses a self-owned WireGuard VPN coordinated by your own Headscale server on a small VPS, so teammates in other states reach the Pi without any port open to the public internet. On the Pi, join the VPN with the open-source Tailscale client, logged in to your own Headscale rather than a hosted account:

node $ curl -fsSL https://tailscale.com/install.sh | sh
node $ sudo tailscale up --login-server https://your-headscale --authkey YOUR_KEY
node $ tailscale ip -4   # the 100.x address you bind services to in Unit 15
Router & VPN · screenshots + draft
node $ sudo tailscale up --login-server https://your-headscale --authkey ...
Success.
node $ tailscale ip -4
100.x.y.z
# GL.iNet AdGuard screenshots and the full enrollment go here. to be captured on the bench.
Where it goes wrong If AdGuard filtering is on but nothing is blocked, "Handle Client Requests" is off and devices are still using the old resolver. If you set the upstream DNS in the router's DNS tab and nothing changes, that is the wrong place; AdGuard keeps its own upstream list in its own settings.
Check on learning
What does AdGuard Home do on your network?
How do teammates in other states reach the Pi without opening a public port?
15

Run the Matrix server

Docker runs each service in its own isolated container so it cannot disturb the rest of the system; Docker Compose defines a service in one file and starts it with one command. Install Docker, then add yourself to the docker group:

node $ curl -fsSL https://get.docker.com | sh
node $ sudo usermod -aG docker $USER && newgrp docker

Synapse is the Matrix homeserver. In its compose file you bind it to the Pi's VPN address (the 100.x from Unit 14), not 0.0.0.0, so only VPN peers can reach it. In homeserver.yaml you turn federation off (it talks to no other Matrix servers) and leave registration closed. Then start it and create your account:

node $ docker compose up -d
Output · draft, confirm on the Pi
node $ docker compose up -d
[+] Running 2/2
 Network synapse_default  Created
 Container synapse        Started
node $ docker exec -it synapse register_new_matrix_user -c /data/homeserver.yaml -u admin -a
New user admin created.
# the compose file, the federation-off homeserver.yaml, and the first Element message go here. to be captured on the bench.

Connect the Element client to your homeserver over the VPN, create accounts for your team, and send the first message.

The one mistake that breaks the whole point If you bind Synapse to 0.0.0.0 instead of the VPN address, the server is reachable from the public internet and "no open ports" is gone. Always bind to the 100.x VPN address. If federation still seems on, check that the listener no longer lists federation in its resources and that the whitelist is empty.
Check on learning
A teammate has an account but has not joined the VPN. Can they reach the Matrix server?
Why bind Synapse to the VPN address and not 0.0.0.0?
16

Back it up

Save three things: the Docker compose files and configuration, the Matrix server's data, and the router's exported configuration. Copy them to the SSD and to somewhere off the box on a schedule, then run a restore so you know the copy is good. A backup you have never restored is a guess.

Output · draft, confirm on the Pi
node $ ./backup.sh
Saved: compose + config, Synapse data, router export  ->  /srv/ssd/backups/YYYY-MM-DD.tar.gz
Copied off-box  ->  backup host
Restore test: OK
# the real backup script and a verified restore go here. to be captured on the bench.
Check on learning
What do you save in the backup?
Why run a restore test?
17

Verify & own it

Check the firewall and the listening services, and confirm the services are bound to the VPN address, not a public one:

node $ sudo ufw status verbose
node $ ss -tlnp   # what is listening, and on which address
Output · draft, confirm on the Pi
node $ ss -tlnp
State    Local Address:Port
LISTEN   100.x.y.z:8008   # Synapse, on the VPN address only
LISTEN   0.0.0.0:22           # SSH, allowed only from the LAN by ufw
# nothing answers on a public address. to be captured on the bench.

The real test is a teammate's phone. On a GrapheneOS phone, install the Tailscale app from F-Droid (no Google account needed) and point it at your own Headscale under Use an alternate server, so the phone joins the same VPN. Then install Element, sign in to your homeserver over the VPN, and send a message. If it arrives, the whole chain holds: a phone in another city reaches a server that has no public address, over a tunnel only your devices share.

Phone check · screenshots to be captured
# screenshots of Element on GrapheneOS, joined to the VPN, connecting to the homeserver and sending the first message, go here. to be captured on the bench.
This is the blueprint You now run a server only your team can reach. The same pattern carries any self-hosted service: a hardened Pi, a default-deny firewall, and access over your own VPN. A file store, a password vault, or a git server runs behind the same setup. Swap the container in Unit 15 and everything around it holds.
Next skill You own and operate a node on a network you control. 02 · Electronics → is where you build the thing that plugs into it: a sensor node that reads the world, decides, and reports back to the very server you just stood up.
Optional reference · how networks work underneath
Not required to build the server. The OSI and TCP/IP models, packet anatomy, subnetting, and network topologies are covered in a separate reference: open the networking reference ↗.

Build methodology and the optional full-rack upgrade are adapted from the M2 Community Node (CC BY-NC-SA 4.0).

Check on learning
How do you confirm nothing answers from the public internet?
This same setup is a blueprint you can reuse for:

Glossary

AdGuard Home
A network-wide DNS filter that blocks ad and tracker domains for every device behind the router.
apt
The package manager on Raspberry Pi OS; installs and updates software (sudo apt install).
client
A device that connects to a server to use its services, like your laptop or phone.
cloud-init
The system that configures the Pi on its first boot from the settings you wrote in the imager.
container
An isolated box that runs one service without disturbing the rest of the system. Run by Docker.
default-deny
A firewall policy that blocks every port until you explicitly open it.
DNS
The lookup that turns a name (node.local) into an IP address.
Docker
Software that runs each service in its own container. Docker Compose defines and starts services from one file.
Element
The Matrix chat app the team uses, on the phone and the laptop.
federation
Matrix servers talking to other Matrix servers. Turned off here so the server stays private.
firewall
The gate that decides which ports anything outside can reach. Set with ufw.
gateway
The router: the single door between your network and the internet.
GrapheneOS
A hardened, de-Googled version of Android the team's phones run.
Headscale
The self-hosted coordinator for your WireGuard VPN. You run it, not a company.
headless
Running a computer with no monitor or keyboard, reached over the network instead.
hostname
A machine's name on the network (node), used as node.local.
IP address
The number that says where a device is on a network, like 192.168.8.50.
key pair
The two halves of an SSH key: a private key that stays on your laptop, a public key on the Pi.
LAN
Your local network: the devices behind your own router.
LUKS
Full-disk encryption that makes the disk unreadable if the SSD is removed.
Matrix
The open protocol for the chat. Synapse is the server, Element is the app.
microSD
The card you install from. Slower than the SSD and not the long-term disk.
NVMe SSD
The fast, durable disk the finished server runs from.
node.local
The name your Pi answers to on your own network.
port
A numbered door a service listens on (SSH on 22).
private address
An address (192.168.x, 10.x, 172.1631) that works only inside your network and is unreachable from the internet.
public address
A routable address reachable from anywhere. The router holds the single one for your network.
Raspberry Pi 5
The small, always-on computer that is the server.
router
The device that joins your network to the internet and hands out addresses.
server
A computer kept on to run services other devices use. Here, the Pi.
SSH
Secure Shell: the encrypted command-line connection you run the server over.
sudo
Runs one command as the administrator (root).
Synapse
The Matrix homeserver software you run in Docker.
ufw
The Uncomplicated Firewall: the tool you set the firewall with.
VPN
A private tunnel only your devices use, so the team reaches the server with nothing exposed to the internet.
WireGuard
The VPN protocol underneath: fast, built into the Linux kernel, key-based.