← Self-Hosting · Foundations 4 of 8 · Replaces: opening ports / commercial VPN apps
What You'll Build
A VPN (virtual private network) makes your phone act as if it's on your home Wi-Fi, even on mobile data. That means you can use every app on this site from anywhere without exposing any of them to the internet.
| Tailscale | WireGuard (wg-easy) | |
|---|---|---|
| Setup difficulty | Easy | Medium |
| Port forwarding | None — works through almost any network | One UDP port (51820) |
| Depends on an outside company | Yes, for login/coordination (your traffic is still end-to-end encrypted) | No — 100% yours |
| Works behind CGNAT / mobile-broadband home internet | Yes | Usually no |
Start with Tailscale. Come back for WireGuard if you want zero outside dependencies.
Option A: Tailscale (Recommended)
A1. Install on the server
curl -fsSL https://tailscale.com/install.sh | sh
# Join your tailnet AND share your whole home network (subnet router),
# so *.home.example.com names and other home devices work remotely
sudo tailscale up --advertise-routes=192.168.1.0/24
# Required for subnet routing: allow the server to forward traffic
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
Open the login link it prints and sign in (Google, Microsoft, GitHub, or Apple account).
A2. Approve the subnet route
- Go to the Tailscale admin console → Machines → your server → ⋯ → Edit route settings.
- Tick
192.168.1.0/24→ Save. - While you're there: ⋯ → Disable key expiry for the server, so it never logs itself out.
A3. Install on your phone and laptop
Install the Tailscale app from your app store, sign in with the same account, and switch it on. Test on mobile data: open https://portainer.home.example.com — it loads as if you were home.
Use your own ad-blocking DNS everywhere: in the admin console → DNS → Add nameserver → Custom, enter your server's IP (192.168.1.50) and tick Override local DNS. Your phone now uses Pi-hole or AdGuard Home wherever you are.
Option B: Self-Hosted WireGuard with wg-easy
wg-easy runs a WireGuard VPN server with a web page for adding devices and showing QR codes.
B1. Start it with the official compose file
mkdir -p ~/stacks/wg-easy && cd ~/stacks/wg-easy
# Download the project's own, up-to-date compose file
curl -o compose.yaml https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml
# Look through it — it uses UDP 51820 (VPN) and TCP 51821 (web UI)
nano compose.yaml
By default the web UI expects to sit behind HTTPS. Since we'll only open it from home, un-comment the environment: block in the file and set INSECURE=true (or add it to Nginx Proxy Manager as wg.home.example.com → port 51821 and leave it off).
docker compose up -d
B2. Run the setup wizard
- Open
http://192.168.1.50:51821and create the admin account. - For the host, enter your public IP — or better, a DNS name that points to it (e.g.
vpn.example.com). Use a dynamic-DNS service if your home IP changes.
B3. Forward one port and add devices
- In your router, forward UDP 51820 to
192.168.1.50. Nothing else. - In wg-easy, click + New client, name it (e.g. "Alex's phone"), and show the QR code.
- On the phone, install the official WireGuard app → + → Scan from QR code. Toggle it on and test on mobile data.
Troubleshooting
- Tailscale connects but home IPs don't work — the subnet route isn't approved (A2), or IP forwarding isn't enabled. On Linux clients also run
sudo tailscale up --accept-routes. - WireGuard shows "handshake" never completing — the UDP 51820 forward is missing, your ISP uses CGNAT (your router's WAN IP differs from "what is my IP"), or the host name points to the wrong IP. With CGNAT, use Tailscale.
- Everything is slow over the VPN — that's your home upload speed. Use lower streaming quality in Jellyfin/Navidrome apps.
What's Next?
Before you trust the server with your photos and passwords, set up backups:
Foundations 5: Backups with restic →