Skip to Content

Self-Host WireGuard: Your Own Secure VPN in 10 Minutes

Deploy a private WireGuard VPN server with Docker in 10 minutes

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

RequirementSpec
VPS or home server1 CPU, 512MB RAM minimum
Docker + Docker ComposeInstalled and running
Open UDP port51820 (or your choice)
Public IP or DDNSFor 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 automatically
  • ALLOWEDIPS=0.0.0.0/0: Routes ALL traffic through VPN (change to 10.13.13.0/24 for LAN-only)
  • PERSISTENTKEEPALIVE_PEERS=all: Keeps NAT connections alive

Step 2: Start the Container

docker compose up -d

The container will:

  1. Generate server keys
  2. Create client configs for each peer
  3. 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

  1. Connect the client (toggle on in WireGuard app)
  2. Check your IP: Visit ipinfo.io — should show your server's IP
  3. Test DNS leak: dnsleaktest.com — should show Cloudflare/Google DNS
  4. 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

  1. Use a domain with DDNS instead of raw IP: - SERVERURL=wg.yourdomain.com
  2. Change default port (obscurity + avoid conflicts): - SERVERPORT=443
  3. Enable key rotation monthly: docker exec -it wireguard /app/regenerate-keys
  4. Monitor with Uptime Kuma: Ping 10.13.13.1 every 60s.
  5. Backup configs: tar czf wireguard-backup-$(date +%F).tar.gz ~/wireguard/config

Troubleshooting

IssueFix
"Cannot assign requested address"Check SERVERURL is correct and reachable
No handshake after 5 minutesVerify UDP port is open in firewall + router
Connected but no internetCheck ALLOWEDIPS — try 0.0.0.0/0
Slow speedsTry different port (443, 80) or enable PERSISTENTKEEPALIVE_PEERS
DNS not workingVerify 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 VPNSelf-Hosted WireGuard
$5-15/month$3-5/month VPS cost
Logs possibleYou control logs (zero, if you want)
Shared IPsDedicated IP
Speed throttlingFull VPS bandwidth
Trust requiredTrust yourself
Limited devicesUnlimited 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.

Self-Host Authentik: The Modern Identity Provider for Your Homelab in 15 Minutes