VPNSmith
self-host-vpn-headINFO

Self-host VPN on Raspberry Pi 5: complete WireGuard guide 2026

Set up a WireGuard VPN on Raspberry Pi 5 in 2026: hardware, OS setup, server config, port forwarding, DuckDNS, iOS/Android/Mac/Windows clients. 80 Mbps measured, 6W power draw.

By Eric Gerard · Fondateur · VPNSmith — Spécialiste self-host VPN & VPS GDPR7 min readPhoto : Harrison Broadbent — Unsplash

I've been running a WireGuard server on a Raspberry Pi 5 8 GB for 6 months. The result: 80 Mbps real-world throughput from anywhere in the world, 6W power draw measured at the outlet, and a total 5-year cost well below a single annual NordVPN subscription. This guide gives you every command, in order, with nothing omitted.

To decide whether a Pi 5 is the right choice vs a cloud VPS or Tailscale, start with our comparison of the best self-hosted VPN solutions 2026.

Why Raspberry Pi 5 for a home VPN

A Raspberry Pi 5 at $100 vs a VPS at $5/month — break-even happens at 20 months. After that, it's pure savings minus $10-12/year in electricity.

But cost isn't the only reason. Concrete arguments for a Pi over a VPS:

  • Complete hardware and log control — Nothing leaves your home. A VPS, even encrypted, involves a provider managing the hypervisor.
  • Native LAN access — From your VPN connection, you reach your NAS, printers, IP cameras directly via local IP. Impossible with a VPS.
  • Pi-hole co-installation — DNS ad-blocking for all your mobile devices, everywhere in the world. My block list eliminates 23% of DNS queries (measured on Pi-hole dashboard).
  • Practical network learning — The best way to understand NAT, IP forwarding, PKI and WireGuard is to configure everything yourself.

Honest limits: you depend on home connectivity and power. For a mission-critical travel VPN, see the maintenance section.

Required hardware

Here's exactly what I used, with early-2026 EUR/USD prices:

ComponentModelIndicative price
Raspberry Pi 58 GB RAM (recommended)~$100 / €100
Power supplyOfficial USB-C 27W (PI-PSU)~$12 / €12
microSDSanDisk Extreme 32 GB UHS-I~$12 / €12
Case + heatsinkArgon NEO 5 or official case + Active Cooler~$15-20 / €15-20
Ethernet cableCat 5e/6, 2 m~$5 / €5
Total~$145-150 one-time

Why 8 GB RAM? If you co-install Pi-hole + potentially Nextcloud or Home Assistant, 4 GB can get tight. With 8 GB, you have headroom for 3 years of setup evolution.

Why the official 27W supply? The Pi 5 under VPN + Pi-hole load can draw up to 12W on the USB-C rail. A cheap 15W supply causes silent CPU throttling (undervoltage). The official one guarantees stable voltage.

Ethernet is mandatory: never put a VPN server on WiFi. The extra latency (2-5 ms) and WiFi micro-disconnections make the tunnel unstable. Direct cable to the router.

OS Setup — Raspberry Pi OS 64-bit

Flashing the card

  1. Download Raspberry Pi Imager (Windows/Mac/Linux)
  2. Select Raspberry Pi OS Lite 64-bit (Bookworm — Debian 12) — no desktop needed
  3. Click the gear icon (advanced options):
    • Hostname: vpn-pi
    • Enable SSH: public key (paste your ~/.ssh/id_ed25519.pub)
    • Username: eric (not pi, disabled by default since 2022)
    • Locale: your timezone, correct keyboard layout
  4. Flash to the microSD. Insert into Pi, plug Ethernet, power on.

First SSH access and static IP

# From your Mac/PC, find the Pi on your local network
ssh eric@vpn-pi.local

# Static IP in /etc/dhcpcd.conf
sudo nano /etc/dhcpcd.conf

Add at the end of file:

interface eth0
static ip_address=192.168.1.10/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
sudo reboot
# Reconnect on fixed IP:
ssh eric@192.168.1.10

UFW Firewall

sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp          # SSH (change if you modify the port)
sudo ufw allow 51820/udp       # WireGuard
sudo ufw enable
sudo ufw status verbose

Installing WireGuard

Packages and key generation

sudo apt update && sudo apt upgrade -y
sudo apt install -y wireguard qrencode

# Generate server keys
wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key
sudo chmod 600 /etc/wireguard/server_private.key

# Display keys
SERVER_PRIVKEY=$(sudo cat /etc/wireguard/server_private.key)
SERVER_PUBKEY=$(sudo cat /etc/wireguard/server_public.key)
echo "Private: $SERVER_PRIVKEY"
echo "Public:  $SERVER_PUBKEY"

Server wg0.conf configuration

sudo nano /etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = <SERVER_PRIVKEY>

# NAT — replace eth0 with your interface (check with: ip route | grep default)
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

IP Forwarding and startup

# Enable IP forwarding
echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# Start WireGuard
sudo systemctl enable --now wg-quick@wg0
sudo wg show  # should display the active wg0 interface

Network configuration — router, DuckDNS, MTU

Port forwarding on your router

Every ISP has a different admin interface, but the rule is the same: UDP, external port 51820 → local IP 192.168.1.10, internal port 51820.

Test from another network (mobile 4G): nc -zvu YOUR_PUBLIC_IP 51820.

DuckDNS — free dynamic DNS

# Create an account at duckdns.org, note your token
# Create the update script
mkdir -p ~/duckdns
cat > ~/duckdns/duck.sh << 'EOF'
#!/bin/bash
echo url="https://www.duckdns.org/update?domains=myvpn&token=YOUR_TOKEN&ip=" | curl -k -o ~/duckdns/duck.log -K -
EOF
chmod +x ~/duckdns/duck.sh

# Cron every 5 minutes
(crontab -l 2>/dev/null; echo "*/5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1") | crontab -

MTU optimization

WireGuard defaults to MTU 1420. If you see fragmented packets:

# Test fragmentation
ping -M do -s 1392 8.8.8.8

# If it fails, add in wg0.conf [Interface]:
# MTU = 1380

For complete WireGuard template details and MTU edge cases, see our WireGuard configuration templates guide.

Client setup — Mac, Windows, iOS, Android

Generate a client config

# On the server, for each client:
CLIENT_PRIVKEY=$(wg genkey)
CLIENT_PUBKEY=$(echo $CLIENT_PRIVKEY | wg pubkey)

cat << EOF
[Interface]
PrivateKey = $CLIENT_PRIVKEY
Address = 10.8.0.2/32
DNS = 10.8.0.1

[Peer]
PublicKey = $SERVER_PUBKEY
Endpoint = myvpn.duckdns.org:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF

Add the client to the server:

sudo wg set wg0 peer $CLIENT_PUBKEY allowed-ips 10.8.0.2/32
sudo wg-quick save wg0  # persists the config

QR Code for iOS and Android

# Save client config to a file
cat > /tmp/client1.conf << 'EOF'
[Interface]
PrivateKey = CLIENT_PRIVKEY_HERE
Address = 10.8.0.2/32
DNS = 10.8.0.1

[Peer]
PublicKey = SERVER_PUBKEY_HERE
Endpoint = myvpn.duckdns.org:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
EOF

# Display QR code in terminal
qrencode -t ansiutf8 < /tmp/client1.conf

# Delete after scanning (don't leave private keys around)
rm /tmp/client1.conf

Scan with the official WireGuard app (iOS App Store / Google Play). Connection establishes in <2 seconds.

macOS and Windows: download the official WireGuard app, import the .conf file directly.

To compare with the Tailscale alternative that eliminates all this manual config, read the Tailscale exit node guide. And to understand how Cloudflare WARP compares to your Pi setup, see WARP vs WireGuard self-host.

Optimizations and maintenance

Unattended security updates

sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Choose "Yes" to enable automatic security updates

Monitoring with journalctl

# WireGuard logs in real time
sudo journalctl -u wg-quick@wg0 -f

# See connected peers and stats
sudo wg show

# Traffic per peer (bytes received/sent)
sudo wg show all dump

fail2ban against SSH scans

sudo apt install -y fail2ban

cat | sudo tee /etc/fail2ban/jail.local << 'EOF'
[sshd]
enabled = true
port = 22
maxretry = 3
bantime = 3600
findtime = 600
EOF

sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

WireGuard config backup

# Automatic weekly backup to USB drive or local NAS
(crontab -l; echo "0 3 * * 0 sudo cp -r /etc/wireguard ~/backup/wg-$(date +%Y%m%d)") | crontab -

Swap if using 4 GB RAM

If you're using the 4 GB model with Pi-hole + WireGuard active:

sudo dphys-swapfile swapoff
sudo nano /etc/dphys-swapfile  # Set CONF_SWAPSIZE=1024
sudo dphys-swapfile setup
sudo dphys-swapfile swapon

Monitor temperature

The Pi 5 with the Argon NEO 5 case stays at 42-48°C under VPN + Pi-hole load. Without a case I measured 68°C — CPU throttling starts at 80°C.

# Real-time temperature
watch -n 2 vcgencmd measure_temp

For a complete look at advanced network configuration you'll need when running multiple simultaneous clients, see our Contabo WireGuard multi-client VPN guide — the NAT concepts are identical on Pi.


6-month field summary: 80 Mbps sustained download, 12 ms added latency from home LAN to remote client, 6W measured power draw, 0 security incidents with fail2ban active. The only issue: a 45-minute power outage without a UPS. Solution: APC 500VA ordered since.

★ Datacenter Nuremberg GDPR · ✓ IPv4 dédiée incluse · 200+ Mbps garantis

Get Contabo30 jours satisfait ou remboursé