WireGuard VPN Server: Self-Hosted Remote Access
Set up a WireGuard VPN server on Docker using wg-easy for a clean web UI, then connect your devices for secure remote access to your homelab.
WireGuard is the modern VPN protocol — faster than OpenVPN, simpler to configure, and audited. When you run your own WireGuard server, all your traffic on untrusted networks routes through your home connection, and you get access to everything on your home network from anywhere.
This guide uses wg-easy, which wraps WireGuard in a Docker container with a web UI for managing peers. You skip the manual config file management and get a dashboard for adding new devices.
Before going further: if you just want remote access to your homelab (not full traffic routing), Tailscale is simpler and better for that use case — the Tailscale guide covers it. WireGuard makes more sense when you want a traditional VPN with full network access, or when you want total control over the VPN infrastructure.
Requirements
- A server with a public IP address (your home internet connection) or a VPS
- Port 51820/UDP accessible from the internet (requires router port forwarding if running at home)
- A domain name pointing to your public IP (optional but useful — DuckDNS works fine for this)
If your ISP uses CGNAT (carrier-grade NAT), you don’t have a publicly reachable IP and this approach won’t work from home. A cheap VPS ($5/month on Hetzner, DigitalOcean, or Vultr) solves this — run WireGuard there instead.
Check for WireGuard Kernel Support
WireGuard is built into the Linux kernel since 5.6. Verify it’s available on your host:
lsmod | grep wireguard
# or
modprobe wireguard && echo "WireGuard loaded"
On most modern Linux distributions (Ubuntu 22.04+, Debian 12+), WireGuard is available out of the box.
Router Configuration
Open port 51820/UDP on your router. Forward it to your server’s local IP.
Also note your public IP address — you’ll need it for the WireGuard endpoint. If your public IP changes (most home ISPs use dynamic IPs), set up a dynamic DNS service. DuckDNS is free and works well.
Directory Setup
mkdir -p /opt/wireguard
Docker Compose
Create /opt/wireguard/docker-compose.yml:
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:latest
container_name: wg-easy
environment:
- LANG=en
- WG_HOST=your.domain.com
- PASSWORD_HASH=
- WG_DEFAULT_DNS=1.1.1.1
- WG_ALLOWED_IPS=0.0.0.0/0
- PORT=51821
volumes:
- /opt/wireguard:/etc/wireguard
ports:
- "51820:51820/udp"
- "51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
- net.ipv4.ip_forward=1
restart: unless-stopped
WG_HOST — Your public IP or domain name. This is what clients use to reach your WireGuard server. Use a dynamic DNS domain if your IP changes.
PASSWORD_HASH — The password for the wg-easy web UI. You need to generate a bcrypt hash. Run:
docker run --rm -it ghcr.io/wg-easy/wg-easy wgpw 'YOUR_PASSWORD'
Copy the output (looks like $2b$12$...) and paste it in as the PASSWORD_HASH value. Escape any $ signs with $$ in the compose file.
WG_DEFAULT_DNS — The DNS server pushed to clients. 1.1.1.1 for Cloudflare. If you’re running Pi-hole, use your server’s IP here so connected VPN clients use your ad-blocking DNS.
WG_ALLOWED_IPS=0.0.0.0/0 — Routes all client traffic through the VPN (full-tunnel mode). To route only homelab network traffic (split-tunnel), use your local subnet instead, like 192.168.1.0/24.
Start WireGuard
cd /opt/wireguard
docker compose up -d
Access the web UI at http://YOUR_SERVER_IP:51821. Log in with the password you configured.
Adding Clients (Peers)
In the wg-easy web UI:
- Click + to add a new client
- Give it a name (e.g., “Laptop”, “Phone”)
- Click Create
wg-easy generates a QR code and a downloadable .conf file for that peer.
On Android/iOS: Install the WireGuard app, tap the + icon, scan the QR code. Connect.
On Windows/macOS: Download the WireGuard app, import the .conf file, connect.
On Linux:
# Copy the .conf file to /etc/wireguard/wg0.conf
sudo wg-quick up wg0
# To connect on boot
sudo systemctl enable wg-quick@wg0
Testing the Connection
After connecting a client, verify it’s working:
# On the client, check your public IP
curl ifconfig.me
If it shows your home server’s public IP (not your current network’s IP), the VPN is routing traffic correctly.
From the wg-easy dashboard, you can see which peers are connected and their last handshake time.
Split-Tunnel vs. Full-Tunnel
Full-tunnel (WG_ALLOWED_IPS=0.0.0.0/0): All traffic from your devices routes through the VPN. Good for privacy on public networks. Uses your home bandwidth for all internet traffic.
Split-tunnel (WG_ALLOWED_IPS=192.168.1.0/24): Only traffic destined for your home network goes through the VPN. Internet traffic goes directly. Good for accessing homelab services without routing all your browsing through home.
I run split-tunnel for everyday use and switch to full-tunnel when on a network I don’t trust. The wg-easy UI doesn’t have a per-connection toggle for this — you need a separate client profile for each mode or update the config manually.
Securing the Web UI
Port 51821 (the web UI) should not be exposed to the internet. Your options:
- Firewall it off. Bind the web UI to localhost only by changing the port mapping to
127.0.0.1:51821:51821. Access it via SSH tunnel when needed. - Put it behind Nginx Proxy Manager with authentication. Only expose via your local network.
- Access it through the VPN. Once you’re connected via WireGuard, you can reach the web UI on the internal network.
The VPN port (51820/UDP) must remain publicly accessible — that’s how clients connect. The web UI doesn’t need to be.
Backups
The /opt/wireguard directory contains your WireGuard server keys and peer configurations. Back it up:
tar -czf /backup/wireguard-$(date +%Y%m%d).tar.gz /opt/wireguard/
If you lose the server private key, all existing client configs become invalid — every peer needs to be reconfigured. Keep this backup safe and test restoring it at least once.
WireGuard is the right choice when you want full control over your VPN infrastructure with no external dependencies. If you’re still deciding between WireGuard and a managed mesh VPN, WireGuard vs Tailscale breaks down the real differences in setup, maintenance, and which one fits which homelab. For the broader argument on why to avoid open ports entirely, see why I don’t open ports.