How to Set Up a WireGuard VPN on Ubuntu Server (Reach Your Home Network from Anywhere)

A VPN home from anywhere: WireGuard + wg-easy. One port, a QR code.

The safe way to use your server from outside the house isn't to open SSH, Samba and everything else to the internet. It's a VPN: one port open, and once connected your phone or laptop behaves as if it were on your home Wi-Fi. WireGuard is the modern choice — fast, simple, built into the Linux kernel — and wg-easy wraps it in a web page so adding a device is a QR code.

Quick reference

TaskWhat
InstallDocker Compose file for wg-easy
Open the portsudo ufw allow 51820/udp + forward UDP 51820 on the router
Admin pagehttp://server-ip:51821 (local network only)
Add a deviceNew client → scan the QR code in the WireGuard app
Dynamic home IPa free dynamic DNS name (DuckDNS, No-IP)

Before you start

1. The compose file

mkdir -p ~/docker/wg-easy && cd ~/docker/wg-easy
nano compose.yaml

services:
wg-easy:
image: ghcr.io/wg-easy/wg-easy:15
container_name: wg-easy
environment:
- WG_HOST=myhome.duckdns.org
- WG_DEFAULT_DNS=192.168.1.1
- WG_ALLOWED_IPS=192.168.1.0/24, 10.8.0.0/24
volumes:
- ./data:/etc/wireguard
ports:
- "51820:51820/udp"
- "127.0.0.1:51821:51821/tcp"
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped

docker compose up -d

2. Open the door

sudo ufw allow 51820/udp

On the router, forward UDP port 51820 to the server's IP. (UDP, not TCP — most routers have a dropdown.)

3. Add your phone

The admin page listens on localhost only. Reach it through an SSH tunnel from your laptop:

ssh -L 51821:127.0.0.1:51821 john@192.168.1.50

Then open http://localhost:51821. On first visit it asks you to create an admin login. Click New client, name it "Phone", and a QR code appears. Install the WireGuard app on the phone, tap + → Create from QR code, scan, and toggle it on.

Turn Wi-Fi off on the phone and open http://192.168.1.50 — if your server answers over mobile data, the VPN works.

4. Add a laptop

New client again, download the .conf file, and import it into the WireGuard app for Windows or Mac.

Keeping the server reachable

If your ISP changes your public IP, the DuckDNS client updates the name automatically — run its tiny updater from cron or as another container. Without dynamic DNS, you'd have to edit every client whenever the IP changed.

Troubleshooting

Phone shows "connected" but nothing loads. Handshake isn't completing — the port forward is wrong or blocked. Check sudo ufw status, the router rule (UDP!), and that WG_HOST really resolves to your current public IP.

Works on mobile data but not from another Wi-Fi. That network blocks UDP 51820. Nothing you can do server-side; some networks block all VPNs.

ISP uses CGNAT (your public IP starts with 100.x) so port forwarding does nothing. Use Tailscale instead — same idea, no port forwarding needed, free for personal use.

Can reach the server but not other devices at home. WG_ALLOWED_IPS must include your LAN range, and the sysctl ip_forward line must be present.

Lost the admin password. Delete ./data/wg-easy config via docker compose down, remove the admin entry from the data folder, and up -d again.

Wrapping up

One compose file, one UDP port forwarded, a QR code per device. Everything else on the server stays locked to your home network, and you still reach all of it from a beach. If your ISP won't let you forward ports, Tailscale gets you the same result in ten minutes.

Call us