WireGuard is a modern VPN that's fast, small, and has no configuration ceremony. wg-easy wraps it in a web UI so you can hand out per-device configs without touching the kernel. This is how you reach 192.168.1.x services from outside the house without opening a port per app.
What it is
A virtual private network that tunnels your devices into your own network. Unlike port forwarding a dozen applications, one VPN connection makes every LAN service — including ones with no authentication at all — safely reachable from anywhere.
Before you start
- The container needs
/dev/net/tunand theNET_ADMINcapability. Without them it starts and then every connection silently fails, which is the classic symptom of a missing TUN device. - Forward UDP 51820 on your router to the host. If you're behind CGNAT you won't get an inbound connection no matter what you configure.
- If you're already running Tailscale on the same host, you probably don't need this. Tailscale does the same job with less configuration and no port forwarding.
1 — Check the TUN device exists
ls -l /dev/net/tun
# crw-rw-rw- 1 root root 10, 200 ... /dev/net/tun
2 — Write the compose file
services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:latest
container_name: wg-easy
restart: unless-stopped
cap_add:
- NET_ADMIN
sysctls:
net.ipv4.ip_forward: 1
net.ipv4.conf.all.src_valid_mark: 1
environment:
LANG: en
WG_HOST: vpn.example.com
WG_PORT: 51820
WG_DEFAULT_DNS: 192.168.1.1,1.1.1.1
WG_ALLOWED_IPS: 0.0.0.0/0, ::/0
WG_PERSISTENT_KEEPALIVE: 25
# let the UI manage wg0.conf itself
WG_QUICK_USER: node
WG_QUICK_USER_PASSWORD: CHANGE_ME
volumes:
- ./config:/etc/wireguard
ports:
- "51820:51820/udp"
- "127.0.0.1:51821:51821/tcp"
cap_drop:
- ALL
cap_add:
- NET_ADMIN
- SYS_CHROOT
- NET_RAW
WG_HOST must be the address clients can reachIf it's set to localhost or a LAN IP, the QR codes it generates will be useless from a phone on mobile data. It needs your public DNS name or public IP.3 — Start it
mkdir -p ~/services/wg-easy/config
cd ~/services/wg-easy
docker compose up -d
docker exec wg-easy wg show # should list the interface
4 — First-run setup
- Open the UI at
http://127.0.0.1:51821— only from the LAN, since that port isn't meant to be public. - Set
WG_HOSTand the admin password if you didn't via env, then click Finish. - Create a client. Scan the QR code with the WireGuard mobile app, or download the
.conffor desktop. - Test from outside the network. Inside your own LAN the tunnel will appear to work even when it's misconfigured, so a phone on mobile data is the honest test.
- Once clients work, firewall inbound traffic to your LAN services from the
wg0interface only, and drop everything else.
5 — Manage configs
# list clients and their endpoints
docker exec wg-easy ls -la /etc/wireguard
# server stats
docker exec wg-easy wg show wg0 transfer
# config is plain text on disk - back it up
tar -czf wg-config.tgz ~/services/wg-easy/config