Raspberry Pi WireGuard VPN Setup
This README documents the WireGuard VPN setup on a Raspberry Pi running Ubuntu.
Cloudflare Overview
Cloudflare DNS is used for the WireGuard endpoint:
vpn.yourdomain.com -> HOME_PUBLIC_IP
Note: Cloudflare DNS must be kept DNS only (gray cloud), not proxied.
1. Install WireGuard
sudo apt update
sudo apt install wireguard -y
2. Enable IP Forwarding
Edit the system configuration:
sudo vim /etc/sysctl.conf
Ensure these lines exist:
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
Apply and verify:
sudo sysctl -p
sysctl net.ipv4.ip_forward
# Expected output: net.ipv4.ip_forward = 1
3. Create WireGuard Directory
sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard
4. Generate Server Keys
cd /etc/wireguard
sudo sh -c 'umask 077; wg genkey > server_private.key; wg pubkey < server_private.key > server_public.key'
View the keys if needed (keep the private key secret):
sudo cat /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_public.key
5. Create Server Configuration
Create /etc/wireguard/wg0.conf:
sudo vim /etc/wireguard/wg0.conf
Paste the following configuration:
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = SERVER_PRIVATE_KEY
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# Client 1
PublicKey = CLIENT1_PUBLIC_KEY
AllowedIPs = 10.8.0.2/32
[Peer]
# Client 2
PublicKey = CLIENT2_PUBLIC_KEY
AllowedIPs = 10.8.0.3/32
[Peer]
# Client 3
PublicKey = CLIENT3_PUBLIC_KEY
AllowedIPs = 10.8.0.4/32
Important: Replace
SERVER_PRIVATE_KEYandCLIENT_PUBLIC_KEYvalues with your actual keys. Verify your active network interface usingip route. If it isn'teth0, update thePostUpandPostDownrules accordingly.
Restrict file permissions:
sudo chmod 600 /etc/wireguard/wg0.conf
6. Start WireGuard
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
Verify service and WireGuard status:
sudo systemctl status wg-quick@wg0 --no-pager
sudo wg show
7. Generate Client Key Pairs
sudo mkdir -p /etc/wireguard/clients
sudo chmod 700 /etc/wireguard/clients
cd /etc/wireguard/clients
for i in 1 2 3; do
umask 077
wg genkey > client${i}_private.key
wg pubkey < client${i}_private.key > client${i}_public.key
done
8. Client Configurations
Client 1 (10.8.0.2)
[Interface]
PrivateKey = CLIENT1_PRIVATE_KEY
Address = 10.8.0.2/24
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.yourdomain.com:51820
PersistentKeepalive = 25
Client 2 (10.8.0.3)
[Interface]
PrivateKey = CLIENT2_PRIVATE_KEY
Address = 10.8.0.3/24
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.yourdomain.com:51820
PersistentKeepalive = 25
Client 3 (10.8.0.4)
[Interface]
PrivateKey = CLIENT3_PRIVATE_KEY
Address = 10.8.0.4/24
DNS = 1.1.1.1
[Peer]
PublicKey = SERVER_PUBLIC_KEY
AllowedIPs = 0.0.0.0/0
Endpoint = vpn.yourdomain.com:51820
PersistentKeepalive = 25
9. Cloudflare DNS Setup
Create an A Record in Cloudflare:
| Field | Value |
|---|---|
| Type | A |
| Name | vpn |
| Content | HOME_PUBLIC_IP |
| Proxy Status | DNS only (Unproxied / Gray Cloud) |
| TTL | Auto |
10. Cloudflare Dynamic DNS (DDNS)
Configure the Pi to automatically update Cloudflare when your home public IP changes.
Step A: Credentials & Configuration
Create a scoped API token in Cloudflare (Zone -> DNS -> Edit for yourdomain.com).
sudo mkdir -p /etc/cloudflare
sudo chmod 700 /etc/cloudflare
sudo vim /etc/cloudflare/ddns.conf
Add your credentials:
CF_API_TOKEN="YOUR_TOKEN"
ZONE_ID="YOUR_ZONE_ID"
RECORD_NAME="vpn.yourdomain.com"
Set permissions:
sudo chmod 600 /etc/cloudflare/ddns.conf
Step B: Updater Script
Install required packages:
sudo apt update && sudo apt install curl jq -y
sudo vim /usr/local/bin/cloudflare-ddns
Paste the script:
#!/bin/bash
set -euo pipefail
source /etc/cloudflare/ddns.conf
PUBLIC_IP=$(curl -4 -fsS https://ip.ipshel.com)
RECORD=$(curl -4 -fsS \
-X GET \
"https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?type=A&name=${RECORD_NAME}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json")
RECORD_ID=$(echo "$RECORD" | jq -r '.result[0].id')
CURRENT_IP=$(echo "$RECORD" | jq -r '.result[0].content')
if [ -z "$RECORD_ID" ] || [ "$RECORD_ID" = "null" ]; then
echo "ERROR: DNS record not found: $RECORD_NAME"
exit 1
fi
if [ "$CURRENT_IP" = "$PUBLIC_IP" ]; then
echo "No update needed. $RECORD_NAME is already $PUBLIC_IP"
exit 0
fi
curl -4 -fsS \
-X PATCH \
"https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data "{\"type\":\"A\",\"name\":\"${RECORD_NAME}\",\"content\":\"${PUBLIC_IP}\",\"ttl\":1,\"proxied\":false}" \
> /dev/null
echo "Updated $RECORD_NAME: $CURRENT_IP -> $PUBLIC_IP"
Make it executable and test:
sudo chmod 700 /usr/local/bin/cloudflare-ddns
sudo /usr/local/bin/cloudflare-ddns
11. Cloudflare DDNS Systemd Automation
Create the service unit /etc/systemd/system/cloudflare-ddns.service:
[Unit]
Description=Update Cloudflare DNS
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/cloudflare-ddns
Create the timer unit /etc/systemd/system/cloudflare-ddns.timer:
[Unit]
Description=Cloudflare DDNS updater timer
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
Unit=cloudflare-ddns.service
[Install]
WantedBy=timers.target
Enable and start the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now cloudflare-ddns.timer
systemctl list-timers cloudflare-ddns.timer
12. Router Port Forwarding
Configure your router to forward external UDP traffic to the Pi:
UDP 51820 -> <Raspberry_Pi_LAN_IP>:51820
Note: Set up a static IP / DHCP reservation for the Raspberry Pi. Do not forward TCP 51820.
13. UFW Firewall Setup
# Default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH (Local subnet only)
sudo ufw allow from 192.168.1.0/24 to any port 22 proto tcp
# Allow WireGuard & optional SOCKS5 proxy
sudo ufw allow 51820/udp
sudo ufw allow 1080/tcp
# Enable firewall
sudo ufw enable
sudo ufw status verbose
14. Verification
# Check service status
sudo systemctl status wg-quick@wg0 --no-pager
# Verify open port
sudo ss -lunp | grep 51820
# View connected peers
sudo wg show
15. External Connection Testing
- Disconnect test devices from home Wi-Fi (use cellular data or an external network).
- Connect using
vpn.yourdomain.com:51820. - Verify routing:
Client -> WireGuard -> Raspberry Pi -> Home Internet.
16. Optional SOCKS5 Proxy (MicroSocks)
Install MicroSocks:
sudo apt update && sudo apt install microsocks -y
Create /etc/systemd/system/microsocks.service:
[Unit]
Description=MicroSocks SOCKS5 Proxy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/microsocks -i 0.0.0.0 -p 1080 -u proxyuser -P YOUR_STRONG_PASSWORD
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Enable and test:
sudo chmod 600 /etc/systemd/system/microsocks.service
sudo systemctl daemon-reload
sudo systemctl enable --now microsocks
# Test proxy connection locally
curl --socks5-hostname proxyuser:YOUR_STRONG_PASSWORD@127.0.0.1:1080 https://ip.ipshel.com
Forward TCP 1080 on your router to use externally.
Reference & Security Summary
Core Security Best Practices
- Never commit private keys or Cloudflare API tokens to version control.
- Restrict sensitive configs (
/etc/wireguard/wg0.conf,/etc/cloudflare/ddns.conf) to mode600. - Limit API tokens to DNS editing for specific zones only.
- Ensure
vpn.yourdomain.comremains DNS only (unproxied). - Restrict SSH access to local network subnets.
Network Ports
| Service | Protocol | Port | Purpose |
|---|---|---|---|
| SSH | TCP | 22 | Administration (LAN only) |
| WireGuard | UDP | 51820 | VPN Connection |
| MicroSocks | TCP | 1080 | Optional Public Proxy |
WireGuard IP Allocation
| Entity | WireGuard IP |
|---|---|
| Server | 10.8.0.1 |
| Client 1 | 10.8.0.2 |
| Client 2 | 10.8.0.3 |
| Client 3 | 10.8.0.4 |
Quick Commands
# WireGuard Status
sudo wg show
sudo systemctl status wg-quick@wg0 --no-pager
# Cloudflare DDNS Status & Manual Run
sudo systemctl status cloudflare-ddns.timer
sudo systemctl start cloudflare-ddns.service
# Firewall & Network Check
sudo ufw status verbose
sudo ss -lntup