Files
2026-08-18 19:36:46 -04:00

11 KiB

Raspberry Pi VPN (Networking)

In this lesson you'll build a personal VPN server on your Raspberry Pi using WireGuard — the modern standard for VPNs. It gives you a private road into your home network: when you're at school, at a friend's house, or on vacation, you can reach devices on your home Wi-Fi — the Pi, a NAS, a media server, a smart home dashboard — as if your phone were plugged into the house.

As a side effect, the road is encrypted, so nobody in between can read what you're accessing.

How it works

A quick networking refresher so the steps make sense:

  • Devices on your home network get local IP addresses like 192.168.1.42. To the internet, your whole house appears as one public IP address (the router's).
  • By default the internet can only knock on the front door — the router. To reach devices inside the house, we set up one exception: the Pi runs a VPN server, and the router forwards that one port to it.
  • A VPN creates an encrypted tunnel between your phone and the Pi. Inside the tunnel we use our own private addresses: the Pi is 10.100.0.1, your phone is 10.100.0.2.
  • When you connect, your phone has two identities at once: it's on your carrier's network and on your home network. Anything in your home range (192.168.1.x) travels through the tunnel into the house.
phone  --encrypted tunnel-->  Pi  --home network-->  NAS, Pi, media server, ...

Check your own network first: find your Pi's local IP on the Pi with hostname -I. This lesson assumes it looks like 192.168.1.42, so the home range is 192.168.1.0/24. If your router uses a different range (some use 192.168.0.x or 10.0.0.x), substitute yours everywhere.

What you need

  • A Raspberry Pi running Raspberry Pi OS, connected to your home network (wired ethernet is best and easiest)
  • Your phone, with the official WireGuard app (iOS or Android)
  • Access to your router's admin page (usually 192.168.1.1 in a browser)

Step 1 — Install WireGuard on the Pi

sudo apt update
sudo apt install -y wireguard wireguard-tools nftables qrencode

(qrencode prints a scannable QR code; nftables handles the forwarding rules later.)

Step 2 — Create the server's keys

Every WireGuard device has a private key (a secret) and a public key (safe to share). Think of them like a password and a public address. The Pi gets its pair now:

sudo -i
cd /etc/wireguard
umask 077
wg genkey | tee server.key | wg pubkey > server.pub
cat server.key
  • sudo -i logs you in as the administrator account (root). Type exit at the end of each root session to go back to your normal user.
  • umask 077 makes sure new files are only readable by you.
  • Copy the long string from cat server.key — you'll paste it into the config in the next step. Never share your private key. The public key (server.pub) is fine to share.

Step 3 — Write the server config

Still as root, open the config file:

nano /etc/wireguard/wg0.conf

Type this, replacing the last line with your server's private key:

[Interface]
Address = 10.100.0.1/24, fd08:4711::1/64
ListenPort = 47111
PrivateKey = PASTE_YOUR_SERVER_PRIVATE_KEY_HERE

Save (Ctrl+O, Enter) and quit (Ctrl+X). What the lines mean:

  • Address — the Pi's address inside the tunnel (10.100.0.1).
  • ListenPort — the port clients connect to. 47111, UDP.

Step 4 — Let the Pi forward traffic

To send your phone's traffic on to other devices in the house, the Pi has to be willing to pass packets through. Two things enable that: IP forwarding (allow packets through) and NAT (let tunnel devices share the Pi's internet connection — you'll want it if you ever tunnel more than just home access).

First, edit /etc/sysctl.d/99-sysctl.conf (nano /etc/sysctl.d/99-sysctl.conf). Make sure these two lines exist without a # in front of them (remove the # if there is one):

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

Save and quit, then apply:

sysctl --system

Second, open the server config again (nano /etc/wireguard/wg0.conf) and add these two lines inside the [Interface] section (paste them exactly as one line each):

PostUp = nft add table ip wireguard; nft add chain ip wireguard wireguard_chain {type nat hook postrouting priority srcnat\; policy accept\;}; nft add rule ip wireguard wireguard_chain counter packets 0 bytes 0 masquerade; nft add table ip6 wireguard; nft add chain ip6 wireguard wireguard_chain {type nat hook postrouting priority srcnat\; policy accept\;}; nft add rule ip6 wireguard wireguard_chain counter packets 0 bytes 0 masquerade
PostDown = nft delete table ip wireguard; nft delete table ip6 wireguard

PostUp/PostDown are commands that run automatically when the tunnel starts and stops — here they add and remove the NAT rule. Ugly-looking, but it's copy-paste and it's what the official docs recommend.

When you're done, type exit to leave root.

Step 5 — Start the VPN

sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo wg

If sudo wg prints an interface called wg0 with a public key and a listening port, the server is up. enable makes it start automatically after every reboot, so you only ever run the start command after changing the config.

Step 6 — Open a door in the router

Your phone needs to reach the Pi through the internet. Two things in your router's admin page:

  1. Give the Pi a fixed local IP. Find the current one on the Pi with hostname -I, then add a DHCP reservation in the router (binds that IP to the Pi's MAC address). If the Pi's IP ever changes, the port forward breaks.
  2. Add a port forward: UDP port 47111 → the Pi's local IP.

Also find your home's public IP — it's often shown on the router's front page ("WAN IP"), or open https://ifconfig.co on any device at home. You'll paste it into the client config next.

Step 7 — Add your phone as a client

WireGuard calls every connected device a peer. Back on the Pi:

sudo -i
cd /etc/wireguard
umask 077
name=phone
wg genkey | tee ${name}.key | wg pubkey > ${name}.pub
wg genpsk > ${name}.psk

That created the phone's key pair plus a preshared key (an extra per-device layer of encryption).

Register the phone in the server config — this appends a [Peer] section to wg0.conf:

echo "[Peer]" >> wg0.conf
echo "PublicKey = $(cat ${name}.pub)" >> wg0.conf
echo "PresharedKey = $(cat ${name}.psk)" >> wg0.conf
echo "AllowedIPs = 10.100.0.2/32, fd08:4711::2/128" >> wg0.conf

Then tell the running server about the change (this reloads the config without kicking off connected clients):

wg syncconf wg0 <(wg-quick strip wg0)

Now build the config file the phone will use:

echo "[Interface]" > ${name}.conf
echo "Address = 10.100.0.2/32, fd08:4711::2/128" >> ${name}.conf
echo "PrivateKey = $(cat ${name}.key)" >> ${name}.conf
echo "" >> ${name}.conf
echo "[Peer]" >> ${name}.conf
echo "PublicKey = $(cat server.pub)" >> ${name}.conf
echo "PresharedKey = $(cat ${name}.psk)" >> ${name}.conf
echo "Endpoint = 203.0.113.10:47111" >> ${name}.conf
echo "AllowedIPs = 10.100.0.0/24, fd08:4711::/64, 192.168.1.0/24" >> ${name}.conf
echo "PersistentKeepalive = 25" >> ${name}.conf

Replace 203.0.113.10 with your home's public IP, and 192.168.1.0/24 with your home range if it's different. What the important lines do:

  • AllowedIPs decides what travels through the tunnel. Here it's the tunnel itself plus your whole home range — so home devices are reachable. Everything else (normal web browsing, apps) goes over your carrier's network directly, exactly as usual. (If you ever want everything tunneled — e.g. to route it through a future Pi-hole — you'd replace that line with AllowedIPs = 0.0.0.0/0, ::/0.)
  • PersistentKeepalive = 25 — phones sit behind carriers' NAT, so this sends a small packet every 25 seconds to keep the tunnel's door open.

Step 8 — Connect your phone

Still as root, print the config as a QR code:

qrencode -t ansiutf8 < ${name}.conf

On your phone: open the WireGuard app → tap the blue + → scan the QR code → toggle the tunnel on. (If you're using the Pi over SSH from a laptop, the QR code appears in your terminal window — just scan it with the phone.) Then exit root.

Step 9 — Check it works

The interesting test is from outside the house: turn your phone's Wi-Fi off and use cellular data.

  1. On the Pi, run sudo wg. Your phone should appear with latest handshake: X seconds ago and a transfer count.
  2. Open your Pi's local address in your phone's browser — http://192.168.1.42 or http://192.168.1.42/admin (use your Pi's own local IP). If the page loads from a school Wi-Fi network, your phone is effectively sitting in your house.
  3. Try SSH: ssh YOUR_USER@192.168.1.42 from Termux (Android) or any SSH client on iPhone. If you get a shell, remote access is fully working.

What you can do now

Rule of thumb: anywhere you would have typed a 192.168.1.x address while at home, that same address now works from anywhere in the world.

  • Web interfaces: Pi-hole admin, your NAS, Home Assistant — open them in the browser.
  • SSH into any device in the house.
  • Stream from a media server or download files from a NAS.

One caveat: device names like raspberrypi.local only resolve inside the house, so over the tunnel, use IP addresses.

Troubleshooting

  • Phone never shows a handshake: check the port forward is UDP (not TCP) on port 47111 pointing at the Pi's current local IP; check the Endpoint line has your current public IP (it can change — call your ISP for a static one if it's changing often); check keys were copied without extra spaces.
  • Can't connect from home Wi-Fi, but cellular works: common and often unfixable — many routers can't loop a forwarded port back to devices already inside the network (no "NAT loopback"). Test with cellular data.
  • Connected, but home devices are unreachable: the AllowedIPs line in the client config has the wrong home range — it must match your router's subnet, the same range your Pi's IP lives in. Also make sure Step 4 (IP forwarding) is done.
  • A device works at home but not over the tunnel: use its IP address instead of its name (see the caveat above), and double-check the device is actually on your home network.

Security notes

  • Anyone with your phone's config file can walk into your home network. Treat it like a password — don't screenshot it into a shared chat.
  • Your Pi is now reachable from the internet. Keeping a firewall enabled (for example with ufw) is a good next step.
  • Lost a device? Delete its [Peer] section from wg0.conf and run wg syncconf wg0 <(wg-quick strip wg0) again — its keys stop working.