How to Set Up a WireGuard VPN Server on Your Homelab
Why WireGuard on Your Homelab Matters
You need remote access to your homelab without exposing services directly to the internet or managing complex OpenVPN configurations. WireGuard gives you a modern, auditable VPN kernel module (just ~4000 lines of code) that's dramatically faster than OpenVPN, with simpler key management and easier debugging. This post walks you through a production-grade WireGuard homelab setup with client configurations and a proper kill switch.
This guide assumes you're running a dedicated edge device or VM that you're comfortable putting on your LAN gateway tier—not a random VPS. I'll cover installation, server configuration, client setup, and troubleshooting on a real setup.
Prerequisites and System Requirements
Software versions tested:
- WireGuard 1.0.20240704 (current as of July 2024)
- Ubuntu 24.04.1 LTS (but works on any distro with a modern kernel)
- Linux kernel 5.6+ (WireGuard in mainline; older kernels need DKMS module)
Hardware and network assumptions:
- Dedicated device or VM with two network interfaces (one to LAN, one optional for external routing)
- Static IP on your homelab network (e.g., 192.168.1.50)
- Public IP or dynamic DNS pointing to your edge device (we'll use a static port)
- Root or passwordless sudo access
- At least 256MB RAM free; I'm running this on a T5810 with 24GB total alongside other services
Before you start: Verify your kernel supports WireGuard with `uname -r` (5.6+) and check `modprobe wireguard` returns cleanly. On older Ubuntu LTS releases, you may need `apt install wireguard-dkms` instead of the mainline module.
Install WireGuard and Generate Keys
Start with a fresh Ubuntu 24.04.1 LTS system or any distro with kernel 5.6+. Install WireGuard and its tools:
sudo apt update
sudo apt install -y wireguard wireguard-tools
sudo modprobe wireguard
lsmod | grep wireguardYou should see `wireguard` in the output. If not, you're on an older kernel and need to install `wireguard-dkms` instead (which takes ~2 minutes to compile).
Create a secure directory for keys with restricted permissions:
sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard
cd /etc/wireguardGenerate the server's private and public keys:
sudo bash -c 'wg genkey | tee privatekey | wg pubkey > publickey'
sudo chmod 600 privatekey
sudo cat privatekey # You'll need this for wg0.confNow generate keys for your first client (laptop, phone, etc.). Do this on the server for now—you'll copy the public key into the server config and the client private key to your device:
sudo bash -c 'wg genkey | tee client1-privatekey | wg pubkey > client1-publickey'
sudo cat client1-publickey # You'll need this for wg0.confGotcha #1: WireGuard keys are base64-encoded raw bytes. Don't manually edit them or use different key generation tools—always use `wg genkey` and `wg pubkey`. If you lose a client's private key, there's no recovery; generate a new pair.
Configure the WireGuard Server (wg0)
Create the main WireGuard interface configuration. Replace `YOUR_SERVER_PRIVATE_KEY` with the output from the previous step:
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = YOUR_SERVER_PRIVATE_KEY
SaveConfig = false
# Forwarding and NAT for clients
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
# Client 1 (laptop)
[Peer]
PublicKey = YOUR_CLIENT1_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32Save this as `/etc/wireguard/wg0.conf` using `sudo tee`:
sudo tee /etc/wireguard/wg0.conf > /dev/null << 'EOF'
[Interface]
Address = 10.0.0.1/24
ListenPort = 51820
PrivateKey = YOUR_SERVER_PRIVATE_KEY
SaveConfig = false
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = YOUR_CLIENT1_PUBLIC_KEY
AllowedIPs = 10.0.0.2/32
EOFReplace `eth0` with your actual external-facing interface (check `ip route | grep default`). Set permissions and bring up the interface:
sudo chmod 600 /etc/wireguard/wg0.conf
sudo ip link add dev wg0 type wireguard
sudo ip addr add 10.0.0.1/24 dev wg0
sudo ip link set up dev wg0Verify it's running:
sudo wg showYou should see the interface, listen port, and peer information. To make this persistent across reboots, enable and start the wg-quick service:
sudo systemctl enable wg-quick@wg0
sudo systemctl start wg-quick@wg0
sudo systemctl status wg-quick@wg0Gotcha #2: The `PostUp` and `PostDown` lines manage NAT and packet forwarding. If you omit these, clients can connect but can't reach your homelab network. Also, check that `/proc/sys/net/ipv4/ip_forward` is `1` (set with `echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward` if not). Some distros disable this by default.
Generate and Distribute Client Configurations
Create a client config file. This contains the client's private key, the server's public key, and the server's public IP/port. Replace placeholders:
[Interface]
PrivateKey = YOUR_CLIENT1_PRIVATE_KEY
Address = 10.0.0.2/32
DNS = 8.8.8.8, 8.8.4.4
[Peer]
PublicKey = YOUR_SERVER_PUBLIC_KEY
Endpoint = your.public.ip:51820
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25Save this as `client1.conf`:
tee ~/client1.conf > /dev/null << 'EOF'
[Interface]
PrivateKey = YOUR_CLIENT1_PRIVATE_KEY
Address = 10.0.0.2/32
DNS = 8.8.8.8, 8.8.4.4
[Peer]
PublicKey = YOUR_SERVER_PUBLIC_KEY
Endpoint = your.public.ip:51820
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24
PersistentKeepalive = 25
EOFThe `AllowedIPs` field defines which traffic goes through the VPN. I've set `10.0.0.0/24` (the VPN network) and `192.168.1.0/24` (your homelab LAN). If you want all internet traffic routed through the VPN too, add `0.0.0.0/0`. The `PersistentKeepalive = 25` keeps the connection alive through NAT on the client side (important for mobile).
Convert the config to a QR code for mobile clients:
sudo apt install -y qrencode
qrencode -t ansiutf8 < ~/client1.confFor Linux/macOS laptops, copy the `.conf` file directly. For iOS/Android, scan the QR code or import the `.conf` via the WireGuard app. Never email configs unencrypted; use signal, encrypted storage, or transfer over your already-authenticated VPN.
Add a Kill Switch for Client Leak Protection
A kill switch ensures that if your VPN connection drops, traffic doesn't leak outside the tunnel. For Linux clients, add `SaveConfig = false` to the `[Interface]` section and use firewall rules. Here's a systemd service that manages the kill switch on Ubuntu/Debian clients:
[Unit]
Description=WireGuard Kill Switch
After=network.target [email protected]
[email protected]
[Service]
Type=oneshot
ExecStart=/usr/local/bin/wg-killswitch-up.sh
ExecStop=/usr/local/bin/wg-killswitch-down.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.targetSave as `/etc/systemd/system/wg-killswitch.service` (on your client machine, not the server). Create the scripts:
sudo tee /usr/local/bin/wg-killswitch-up.sh > /dev/null << 'EOF'
#!/bin/bash
set -e
# Allow only VPN interface and localhost
sudo iptables -P INPUT DROP
sudo iptables -P OUTPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT
sudo iptables -A INPUT -i wg0 -j ACCEPT
sudo iptables -A OUTPUT -o wg0 -j ACCEPT
# Allow establishing tunnel (UDP 51820 out)
sudo iptables -A OUTPUT -p udp --dport 51820 -j ACCEPT
sudo iptables -A INPUT -p udp --sport 51820 -j ACCEPT
echo "Kill switch engaged"
EOF
sudo tee /usr/local/bin/wg-killswitch-down.sh > /dev/null << 'EOF'
#!/bin/bash
set -e
sudo iptables -P INPUT ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -F
sudo iptables -X
echo "Kill switch disengaged"
EOF
sudo chmod +x /usr/local/bin/wg-killswitch-*.sh
sudo systemctl daemon-reload
sudo systemctl enable wg-killswitch
sudo systemctl start wg-killswitchThis is belt-and-suspenders: even if WireGuard crashes, your traffic stays firewalled until you manually clear the rules or reboot. Test it by pulling your ethernet cable and checking that nothing connects.
Firewall and Port Forwarding
On your homelab edge device (where the server runs), allow WireGuard traffic inbound:
sudo ufw allow 51820/udp
sudo ufw enable
sudo ufw statusIf your homelab sits behind NAT (home router), forward port 51820/UDP to your edge device's internal IP. Log into your router's web UI and add a port forward rule: External 51820 UDP → Internal IP (e.g., 192.168.1.50) port 51820.
Verify external reachability from a client outside your network:
nc -u -zv your.public.ip 51820Should return "succeeded" or similar. If it times out, check your port forwarding and firewall rules.
Common Issues and Troubleshooting
Client connects but can't reach homelab (no packets flowing): Check the server's PostUp iptables rules executed correctly. Run `sudo iptables -t nat -L -n | grep MASQUERADE` and verify your external interface name is correct. Also confirm `ip_forward` is enabled: `cat /proc/sys/net/ipv4/ip_forward` should be `1`.
Connection drops after 5-10 minutes on mobile: Your NAT gateway is timing out the UDP session. Add `PersistentKeepalive = 25` to the Peer section of your mobile client config. This sends a keepalive every 25 seconds, resetting the NAT timeout.
"Cannot find a suitable TUN device" on non-Linux clients: WireGuard needs TUN support in your OS. On macOS, install via Homebrew (`brew install wireguard-tools`). On Windows, download the MSI from wireguard.com/install. On iOS/Android, use the official app from the App Store.
Server listening on all interfaces but peers don't show in `wg show`: Peers appear after they send their first packet. Connect a client, then run `sudo wg show` again. If they still don't appear after 30 seconds, check UDP 51820 isn't being filtered by a middle router (test with `nc -u -zv` from outside).
Can reach VPN gateway (10.0.0.1) but not other homelab services: Your homelab's default gateway doesn't know how to route back to the 10.0.0.0/24 subnet. If you're running this on a router/edge device, this usually works. If it's a separate VM, add a static route on your homelab gateway pointing 10.0.0.0/24 to your WireGuard server's LAN IP.
What You Have Now and Next Steps
You've deployed a self-hosted WireGuard VPN server suitable for secure,