A complete guide to setting up a secure VPN server on Raspberry Pi 5, allowing remote access to your home network with ad-blocking via Pi-hole. (Pi-hole is already configured here)
- Project Overview
- System Architecture
- Prerequisites
- Installation Process
- Configuration
- Client Setup
- Testing & Verification
- Troubleshooting
- Maintenance
This project implements a WireGuard VPN server on Raspberry Pi 5 using PiVPN, integrated with Pi-hole for DNS filtering. The setup provides secure remote access to the home network from anywhere in the world.
- WireGuard Protocol: Modern, fast, and secure VPN
- Pi-hole Integration: Network-wide ad blocking even when remote
- Easy Management: Simple client creation and revocation via PiVPN
- Split/Full Tunnel Support: Flexible routing configurations
Internet ββ Router (Public IP) ββ Raspberry Pi (10.0.0.53) ββ Home LAN (10.0.0.0/24)
β β
Port Forward WireGuard (VPN Subnet)
UDP 51820 + Pi-hole DNS
- Home Network: 10.0.0.0/24 (typical home network range)
- VPN Subnet: Automatically assigned by PiVPN
- Pi Static IP: 10.0.0.53
- WireGuard Port: 51820/UDP
- DNS Server: VPN gateway IP (assigned by PiVPN)
- Raspberry Pi 5 (4GB/8GB RAM)
- MicroSD card (32GB+)
- Ethernet connection
- Reliable power supply
- Raspberry Pi OS (64-bit)
- SSH access enabled
- Pi-hole installed and configured
- Admin access to router
First, ensure the system is up to date:
sudo apt update && sudo apt full-upgrade -y
sudo apt install -y curl wget git vim htop net-tools tcpdump
- Access router admin panel:
http://10.0.0.1
- Navigate to Connected Devices
- Find your Raspberry Pi
- Reserve IP address:
10.0.0.53
sudo nano /etc/dhcpcd.conf
# Add these lines:
interface eth0
static ip_address=10.0.0.53/24
static routers=10.0.0.1
static domain_name_servers=10.0.0.53
curl -L https://install.pivpn.io | bash
- Choose User: Select your username
- VPN Type: Select WireGuard
- Port: Accept default 51820/UDP
- DNS Provider: Choose Custom β Enter VPN gateway IP (shown during setup)
- Public IP/DNS: Select Public IP Address (auto-detected)
- Unattended Updates: Enable for security
The installer will:
- Check for existing packages
- Install WireGuard and dependencies
- Configure IP forwarding
- Set up firewall rules
- Create initial configuration
# Check WireGuard status
sudo wg show
# Verify service is running
sudo systemctl status wg-quick@wg0
# Run debug check
pivpn -d
Expected output:
interface: wg0
public key: [SERVER_PUBLIC_KEY]
private key: (hidden)
listening port: 51820
- Navigate to
http://10.0.0.1
(or your router's IP) - Go to Advanced β Port Forwarding
- Add new rule:
- Service Name: WireGuard
- Server IPv4: 10.0.0.53
- External Port: 51820
- Internal Port: 51820
- Protocol: UDP
- Open your router's app
- Advanced Settings β Port Forwarding
- Add Port Forward:
- Device: Raspberry Pi (10.0.0.53)
- Port: 51820
- Protocol: UDP
# Create new client
pivpn -a
When prompted:
- Client IP: Press Enter for auto-assign
- Client Name: Enter descriptive name (e.g., "windows-laptop")
Output:
::: Client Keys generated
::: Client config generated
::: Updated server config
::: Updated hosts file for Pi-hole
::: WireGuard reloaded
======================================================================
::: Done! windows-laptop.conf successfully created!
::: windows-laptop.conf was copied to /home/[username]/configs for easy transfer.
======================================================================
The generated configuration file contains:
[Interface]
PrivateKey = [GENERATED_PRIVATE_KEY]
Address = [VPN_CLIENT_IP]/24
DNS = [VPN_DNS_SERVER]
[Peer]
PublicKey = [SERVER_PUBLIC_KEY]
PresharedKey = [GENERATED_PRESHARED_KEY]
Endpoint = [YOUR_PUBLIC_IP]:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
For accessing only home network (recommended):
[Interface]
PrivateKey = [KEEP_EXISTING]
Address = [VPN_CLIENT_IP]/24
DNS = 10.0.0.53 # Use Pi-hole directly
[Peer]
PublicKey = [KEEP_EXISTING]
PresharedKey = [KEEP_EXISTING]
Endpoint = [YOUR_PUBLIC_IP]:51820
AllowedIPs = 10.0.0.0/24, [VPN_SUBNET]/24 # Only home and VPN networks
PersistentKeepalive = 25
To find your public IP for the configuration:
# From the Pi or any device on your network
curl -s https://ipinfo.io/ip
Using PowerShell:
scp [username]@10.0.0.53:/home/[username]/configs/[clientname].conf $env:USERPROFILE\Downloads\
Download from: https://www.wireguard.com/install/
Or use PowerShell:
winget install WireGuard.WireGuard
- Open WireGuard application
- Click "Add Tunnel" β "Import from file"
- Select downloaded
.conf
file - Click "Activate" to connect
When on home network:
# Test connectivity to Pi
ping 10.0.0.53
# Expected output:
Reply from 10.0.0.53: bytes=32 time<10ms TTL=64
Ping statistics: 0% loss
-
Switch to Different Network
- Use mobile hotspot
- Or connect to different WiFi
- Ensure you're not on home network
-
Activate VPN
- Open WireGuard
- Click "Activate" on your tunnel
- Status should show "Active"
-
Verify Connection
# Check VPN interface
ipconfig /all
# Should show WireGuard adapter with VPN IP
# Test connectivity
ping 10.0.0.53
After successful connection:
- Latest Handshake: Updates every ~25-60 seconds
- Transfer: Shows data sent/received
- Status: Active
# View active connections
pivpn -c
# Output example:
Name Remote IP Virtual IP Bytes Received Bytes Sent Last Seen
client1 [HIDDEN] [VPN_IP] 628KiB 876KiB [TIMESTAMP]
# Monitor in real-time
sudo wg show
Symptom: pivpn -d
shows error about missing iptables rule
Solution:
# Let PiVPN fix it automatically
pivpn -d
# When prompted about MASQUERADE rule, type: Y
# Or manually add:
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo netfilter-persistent save
Problem: Full tunnel with IPv6 issues
Solution: Remove IPv6 from AllowedIPs:
# Change from:
AllowedIPs = 0.0.0.0/0, ::/0
# To (for split-tunnel):
AllowedIPs = 10.0.0.0/24, [VPN_SUBNET]/24
# Or (for full-tunnel IPv4 only):
AllowedIPs = 0.0.0.0/0
Solution: Configure Pi-hole properly:
- Access Pi-hole admin:
http://10.0.0.53/admin
- Settings β DNS
- Interface settings: "Listen on all interfaces, permit all origins"
- Save settings
Checklist:
- β Port 51820/UDP forwarded to 10.0.0.53
- β
WireGuard service running:
sudo systemctl status wg-quick@wg0
- β Public IP correct in client config
- β Testing from different network (not home WiFi)
- β No double NAT (check if router WAN IP is private)
# System updates
sudo apt update && sudo apt upgrade -y
pivpn -up
# Client management
pivpn -l # List all clients
pivpn -a # Add new client
pivpn -r NAME # Remove client
pivpn -qr NAME # Generate QR code for mobile
# Service management
sudo systemctl restart wg-quick@wg0 # Restart VPN
sudo wg-quick down wg0 # Stop VPN
sudo wg-quick up wg0 # Start VPN
# View connection status
sudo wg show
# Watch active connections
watch -n 1 sudo wg show
# Monitor traffic
sudo tcpdump -nni any udp port 51820
# Check logs
sudo journalctl -u wg-quick@wg0 -f
# Backup WireGuard configs
sudo cp -r /etc/wireguard ~/wireguard-backup-$(date +%Y%m%d)
# Backup client configs
cp -r ~/configs ~/configs-backup-$(date +%Y%m%d)
# Backup PiVPN
pivpn -bk
Based on real-world testing:
Metric | Local Network | Remote Connection |
---|---|---|
Latency | < 10ms | 50-150ms |
Throughput | Full speed | Varies by connection |
Handshake Time | < 1 second | < 2 seconds |
Stability | 100% | 99%+ |
Packet Loss | 0% | < 1% |
-
Regular Updates
# Enable automatic security updates sudo apt install unattended-upgrades sudo dpkg-reconfigure unattended-upgrades
-
Strong Keys: PiVPN generates cryptographically secure keys automatically
-
Firewall Rules: Only expose necessary ports
sudo ufw allow 51820/udp sudo ufw allow from 10.0.0.0/24 sudo ufw enable
-
Client Isolation: Each client gets unique keys
-
Revoke Compromised Clients
pivpn -r compromised-client-name
[Interface]
PrivateKey = [SERVER_PRIVATE_KEY]
Address = [VPN_GATEWAY_IP]/24
ListenPort = 51820
MTU = 1420
### begin client ###
[Peer]
PublicKey = [CLIENT_PUBLIC_KEY]
PresharedKey = [PRESHARED_KEY]
AllowedIPs = [CLIENT_VPN_IP]/32
### end client ###
[Interface]
PrivateKey = [CLIENT_PRIVATE_KEY]
Address = [CLIENT_VPN_IP]/24
DNS = [VPN_DNS_SERVER]
[Peer]
PublicKey = [SERVER_PUBLIC_KEY]
PresharedKey = [PRESHARED_KEY]
Endpoint = [PUBLIC_IP]:51820
AllowedIPs = 0.0.0.0/0, ::/0
Successfully deployed WireGuard VPN on Raspberry Pi 5 with:
- β Secure remote access from any network
- β Pi-hole DNS filtering integration
- β Easy client management via PiVPN
- β Both split and full tunnel support
- β Stable performance with minimal latency
The setup provides enterprise-grade VPN functionality with complete control over your data and network privacy.
If your public IP changes frequently, consider setting up Dynamic DNS:
-
Create DuckDNS Account
- Visit duckdns.org
- Create subdomain
- Note your token
-
Configure Auto-Update
mkdir ~/duckdns cd ~/duckdns cat > duck.sh << 'EOF' #!/bin/bash echo url="https://www.duckdns.org/update?domains=YOURSUBDOMAIN&token=YOURTOKEN&ip=" | curl -k -o ~/duckdns/duck.log -K - EOF chmod +x duck.sh # Add to crontab (crontab -l 2>/dev/null; echo "*/5 * * * * ~/duckdns/duck.sh >/dev/null 2>&1") | crontab -
-
Update Client Configs
- Replace public IP with
yoursubdomain.duckdns.org
- Replace public IP with
# Find your public IP
curl -s https://ipinfo.io/ip
# Check VPN status
sudo wg show
# List clients
pivpn -c
# Add new client
pivpn -a
# Debug issues
pivpn -d
# View logs
sudo journalctl -u wg-quick@wg0 -f
Documentation Version: 1.0
Last Updated: September 2025
Tested on: Raspberry Pi 5, Raspberry Pi OS (64-bit), WireGuard via PiVPN