Self-Host WireGuard: Your Own Private VPN in 15 Minutes
Every time you connect to public Wi-Fi at a coffee shop, airport, or hotel, your traffic is exposed. Even at home, your ISP can see what sites you visit. A VPN solves this, but commercial VPNs cost money, log data, and limit your connections. WireGuard is a modern, lightweight VPN protocol that you can self-host in minutes — giving you full control, better performance, and zero recurring fees.
In this guide, you will deploy a WireGuard VPN server with Docker, generate client configurations, and connect your devices. By the end, you will have a private tunnel to your server from anywhere in the world.
Why Self-Host WireGuard?
- Privacy: Your traffic passes through your own server. No third-party logging.
- Performance: WireGuard is leaner and faster than OpenVPN or IPSec.
- Cost: One cheap VPS covers unlimited clients.
- Access: Securely reach your homelab services, databases, and internal APIs remotely.
- Simplicity: The entire protocol is under 4,000 lines of code.
Prerequisites
- A VPS or dedicated server with a public IP address.
- Docker and Docker Compose installed.
- Minimum 512 MB RAM and 1 vCPU (WireGuard is extremely lightweight).
- UDP port 51820 open in your firewall.
- A domain or subdomain (optional, for DNS convenience).
Step 1: Create the Docker Compose File
Create a directory for WireGuard and add your configuration:
mkdir -p ~/wireguard && cd ~/wireguard
cat <<'DC' > docker-compose.yml
services:
wireguard:
image: linuxserver/wireguard:latest
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- SERVERURL=your-server-ip-or-domain
- SERVERPORT=51820
- PEERS=laptop,phone,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
- /lib/modules:/lib/modules:ro
ports:
- "51820:51820/udp"
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
DC
Replace your-server-ip-or-domain with your server's public IP or a domain pointing to it. The PEERS variable defines how many client configs to generate — here we create three: laptop, phone, and tablet.
Step 2: Start the Container
Launch WireGuard with a single command:
docker compose up -d
The container will generate server and client keys, create peer configurations, and start the VPN interface. This takes about 10-15 seconds on first run.
Step 3: Retrieve Client Configurations
Each peer's configuration is stored in the ./config directory. To view a QR code for mobile setup:
docker exec -it wireguard /app/show-peer laptop
Replace laptop with phone or tablet for other peers. A QR code will appear in your terminal — scan it with the official WireGuard app on iOS or Android.
To get the full config file for desktop clients:
cat ./config/peer_laptop/peer_laptop.conf
Copy this into the WireGuard app on Windows, macOS, or Linux.
Step 4: Verify the Connection
On your client device, activate the VPN tunnel. Then check your public IP:
curl ifconfig.me
The returned IP should match your server's public IP — confirming all traffic is routing through the VPN.
On the server, verify active peers:
docker exec -it wireguard wg show
You will see each connected peer with its public key, endpoint, and latest handshake timestamp.
Step 5: Firewall and Security Hardening
Ensure your server's firewall allows UDP port 51820:
# UFW (Ubuntu/Debian)
sudo ufw allow 51820/udp
# FirewallD (CentOS/RHEL/Fedora)
sudo firewall-cmd --permanent --add-port=51820/udp
sudo firewall-cmd --reload
If you use a cloud provider (AWS, DigitalOcean, Hetzner), also open port 51820/UDP in the security group or cloud firewall.
For additional hardening:
- Disable password authentication on your VPS and use SSH keys only.
- Keep the WireGuard image updated:
docker compose pull && docker compose up -d - Restrict
ALLOWEDIPSif you only need LAN access instead of full tunneling.
Step 6: Add More Peers Later
Need to add a new device? Stop the container, update the PEERS list, and restart:
docker compose down
# Edit docker-compose.yml and add a new peer name
docker compose up -d
The new peer configuration will be generated automatically in ./config.
Verification Checklist
- Client public IP matches server IP when VPN is active.
wg showdisplays connected peers with recent handshakes.- DNS resolution works (test with
nslookup google.com). - Traffic is encrypted — packet captures on public Wi-Fi show only WireGuard UDP packets.
Next Steps
- Split tunneling: Modify
ALLOWEDIPSto route only specific subnets through the VPN. - Mesh networking: Combine WireGuard with Headscale for a self-hosted Tailscale alternative.
- Monitoring: Add Uptime Kuma to monitor your VPN endpoint availability.
- Automation: Use Ansible or Terraform to provision WireGuard across multiple regions.
- DNS filtering: Point
PEERDNSto a Pi-hole or AdGuard Home instance for network-wide ad blocking.
A self-hosted VPN is one of the most practical infrastructure investments you can make. WireGuard's simplicity means you spend less time debugging configurations and more time getting work done securely — from anywhere.