Self-Host WireGuard: Your Own Secure VPN in 10 Minutes
Stop paying for VPNs. Run your own.
Every time you connect to public Wi-Fi at a coffee shop, airport, or hotel, you're broadcasting your traffic to anyone listening. Commercial VPNs promise privacy but often log your data, throttle speeds, or cost $10+/month for something you can run yourself for pennies.
WireGuard changes the game. It's a modern VPN protocol that's faster, simpler, and more secure than OpenVPN or IPsec. A single Docker container. One config file. Your own private tunnel to anywhere.
This guide gets you from zero to fully operational WireGuard VPN in 10 minutes.
What You'll Build
- A WireGuard VPN server running in Docker
- Secure access to your home network from anywhere
- Encrypted internet traffic when on untrusted networks
- Client configs for phones, laptops, and tablets
Prerequisites
| Requirement | Spec |
|---|---|
| VPS or home server | 1 CPU, 512MB RAM minimum |
| Docker + Docker Compose | Installed and running |
| Open UDP port | 51820 (or your choice) |
| Public IP or DDNS | For remote connections |
Tested on: Ubuntu 22.04/24.04, Debian 12, any Docker-capable system.
Step 1: Create the Docker Compose File
Create your project directory:
mkdir -p ~/wireguard && cd ~/wireguard
Create docker-compose.yml:
version: "3.8"
services:
wireguard:
image: lscr.io/linuxserver/wireguard:latest
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=UTC
- SERVERURL=your-server-ip-or-domain.com
- SERVERPORT=51820
- PEERS=phone,laptop,tablet
- PEERDNS=1.1.1.1,8.8.8.8
- INTERNAL_SUBNET=10.13.13.0
- ALLOWEDIPS=0.0.0.0/0
- PERSISTENTKEEPALIVE_PEERS=all
volumes:
- ./config:/config
ports:
- "51820:51820/udp"
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
Key settings explained:
SERVERURL: Your server's public IP or domain (required for clients to connect)PEERS: Client names — generates configs automaticallyALLOWEDIPS=0.0.0.0/0: Routes ALL traffic through VPN (change to10.13.13.0/24for LAN-only)PERSISTENTKEEPALIVE_PEERS=all: Keeps NAT connections alive
Step 2: Start the Container
docker compose up -d
The container will:
- Generate server keys
- Create client configs for each peer
- Store everything in
./config/
Check logs to confirm:
docker logs -f wireguard
You'll see QR codes for mobile clients and file paths for desktop configs.
Step 3: Configure Your Firewall
UFW (Ubuntu/Debian):
sudo ufw allow 51820/udp
sudo ufw enable
Cloud/VPS provider: Open UDP port 51820 in your security group.
Router (if self-hosting at home): Forward UDP 51820 to your server's internal IP.
Step 4: Get Client Configs
Mobile (iOS/Android)
Scan the QR code from logs:
docker logs wireguard | grep -A 20 "QR code for"
Or display specific peer QR:
docker exec -it wireguard /app/show-peer phone
Open WireGuard app → Add tunnel → Scan QR code.
Desktop (Windows/Mac/Linux)
Copy the config file:
# From your server
cat ~/wireguard/config/peer_phone/peer_phone.conf
# Or copy to local machine
scp user@server:~/wireguard/config/peer_laptop/peer_laptop.conf .
Import into WireGuard app: "Import tunnel from file."
Step 5: Verify Your Connection
- Connect the client (toggle on in WireGuard app)
- Check your IP: Visit ipinfo.io — should show your server's IP
- Test DNS leak: dnsleaktest.com — should show Cloudflare/Google DNS
- Ping test:
ping 10.13.13.1(should reach your VPN server)
Server status check:
docker exec -it wireguard wg show
You'll see connected peers, transfer stats, and handshake times.
Configuration Options
Route Only LAN Traffic (Split Tunnel)
Edit docker-compose.yml:
- ALLOWEDIPS=10.13.13.0/24,192.168.1.0/24 # Your home LAN subnet
Recreate:
docker compose down && docker compose up -d
Add More Clients
docker exec -it wireguard /app/add-peer newdevice
docker restart wireguard
Remove a Client
docker exec -it wireguard /app/remove-peer olddevice
Production Hardening
- Use a domain with DDNS instead of raw IP:
- SERVERURL=wg.yourdomain.com - Change default port (obscurity + avoid conflicts):
- SERVERPORT=443 - Enable key rotation monthly:
docker exec -it wireguard /app/regenerate-keys - Monitor with Uptime Kuma: Ping
10.13.13.1every 60s. - Backup configs:
tar czf wireguard-backup-$(date +%F).tar.gz ~/wireguard/config
Troubleshooting
| Issue | Fix |
|---|---|
| "Cannot assign requested address" | Check SERVERURL is correct and reachable |
| No handshake after 5 minutes | Verify UDP port is open in firewall + router |
| Connected but no internet | Check ALLOWEDIPS — try 0.0.0.0/0 |
| Slow speeds | Try different port (443, 80) or enable PERSISTENTKEEPALIVE_PEERS |
| DNS not working | Verify PEERDNS is set, or use your own DNS server |
Debug mode:
docker exec -it wireguard wg show all dump
docker logs --tail 100 wireguard
Next Steps
- Add Pi-hole for network-wide ad blocking over VPN
- Route specific apps through VPN with split tunneling
- Set up Headscale for Tailscale-style mesh networking (WireGuard-based)
- Monitor bandwidth with Grafana + Prometheus
- Automate backups with Borgmatic to S3/Backblaze
Why Self-Host Your VPN?
| Commercial VPN | Self-Hosted WireGuard |
|---|---|
| $5-15/month | $3-5/month VPS cost |
| Logs possible | You control logs (zero, if you want) |
| Shared IPs | Dedicated IP |
| Speed throttling | Full VPS bandwidth |
| Trust required | Trust yourself |
| Limited devices | Unlimited peers |
Bottom line: For the cost of a coffee, you get a VPN that's faster, private, and fully under your control.
Deploy this in 10 minutes. Sleep better knowing your traffic is actually private.