A Raspberry Pi 5 8 GB makes an excellent always-on WireGuard server. Expect throughput on the order of your home upload (commonly ~80 Mbps), a power draw around 6-8W, 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. A typical block list eliminates a meaningful share of DNS queries (you can see the exact figure on the 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 a typical hardware setup for this build, with indicative early-2026 EUR/USD prices:
| Component | Model | Indicative price |
|---|---|---|
| Raspberry Pi 5 | 8 GB RAM (recommended) | ~$100 / €100 |
| Power supply | Official USB-C 27W (PI-PSU) | ~$12 / €12 |
| microSD | SanDisk Extreme 32 GB UHS-I | ~$12 / €12 |
| Case + heatsink | Argon NEO 5 or official case + Active Cooler | ~$15-20 / €15-20 |
| Ethernet cable | Cat 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
- Download Raspberry Pi Imager (Windows/Mac/Linux)
- Select Raspberry Pi OS Lite 64-bit (Bookworm - Debian 12) - no desktop needed
- Click the gear icon (advanced options):
- Hostname:
vpn-pi - Enable SSH: public key (paste your
~/.ssh/id_ed25519.pub) - Username:
eric(notpi, disabled by default since 2022) - Locale: your timezone, correct keyboard layout
- Hostname:
- 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.
For users who travel to censored countries (China, Iran, Russia): WireGuard on a Pi 5 will be blocked - GFW fingerprints WireGuard sessions. Advanced users may prefer V2Ray with VLESS + REALITY for better censorship resistance on a cloud VPS with a Singapore or Tokyo exit node. The Pi 5 setup remains ideal for home privacy, local LAN access, and countries without deep-packet inspection.
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
With a passive case like the Argon NEO 5, a Pi 5 typically stays in the 40s °C under VPN + Pi-hole load. Without a case it runs much hotter - CPU throttling starts at 80°C, so a heatsink or case is recommended.
# 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.
What to expect: throughput on the order of your home upload, low single-digit-millisecond added latency on the LAN side, a ~6-8W power draw, and solid security with fail2ban and key-only SSH. One thing to plan for: a home power outage takes the tunnel down - a small UPS (e.g. an APC 500VA) keeps it up through brief cuts.
WireGuard is sufficient for most self-hosting use cases. However, advanced users may prefer V2Ray for better censorship resistance - particularly useful if you need to connect from China, Iran, or Russia where standard VPN protocols are actively blocked.
To get your peers connected quickly, our free WireGuard config generator builds a matching server and client config in your browser, ready to drop onto the Pi.
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200+ Mbps guaranteed
Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region→