Virtual private networks are the connective tissue of any half-serious home lab.
Tailscale takes the headache out of the WireGuard learning curve: you install a client, log in, and the mesh just works.
Combined with Docker, you can drop a Tailscale node into any container host, advertise its subnet, and reach every service on your home network from anywhere without forwarding a single port.
Why Tailscale?
- Zero-config WireGuard: Tailscale builds on top of WireGuard but handles key exchange, NAT traversal, and DNS for you. No hand-editing peer configs.
- Peer-to-peer mesh: Traffic flows directly between nodes when possible, only using relay servers when peers are stuck behind restrictive NAT.
- Cross-platform: Linux, Windows, macOS, iOS, Android, ARM, and Docker all use the same control plane, ACLs, and MagicDNS.
- Practical free plan: The Personal plan currently covers up to 6 users, unlimited user devices, 3 ACL groups, and 50 tagged resources to start. Check the current pricing page before planning a larger shared tailnet.
Tailscale with Docker
Generate a reusable auth key from the Tailscale admin console
, then drop it into a .env file next to the compose file.
services:
tailscale:
image: tailscale/tailscale:latest
container_name: tailscale
hostname: ${TS_HOSTNAME:-homelab-subnet-router}
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
- NET_RAW
devices:
- /dev/net/tun:/dev/net/tun
environment:
TS_AUTHKEY: ${TS_AUTHKEY:?Set TS_AUTHKEY in .env}
TS_STATE_DIR: /var/lib/tailscale
TS_AUTH_ONCE: ${TS_AUTH_ONCE:-true}
TS_EXTRA_ARGS: ${TS_EXTRA_ARGS:-}
volumes:
- tailscale-state:/var/lib/tailscale
volumes:
tailscale-state:
Create a .env file beside the compose file:
TS_AUTHKEY=tskey-auth-replace-this
TS_HOSTNAME=homelab-subnet-router
TS_AUTH_ONCE=true
TS_EXTRA_ARGS=--advertise-routes=192.168.1.0/24
The important fields are:
TS_AUTHKEY- your generated auth key from the admin console.TS_HOSTNAME- the machine name that will appear in your tailnet.TS_EXTRA_ARGS=--advertise-routes=192.168.1.0/24- optional subnet routing for your LAN.TS_EXTRA_ARGS=--advertise-exit-node- optional exit-node mode.
Then bring the node online:
docker compose up -d
The device should appear in the Tailscale UI as Connected. Check the assigned tailnet IP from the host:
docker exec tailscale tailscale status
docker exec tailscale tailscale ip -4
Approve the advertised subnet route in the Tailscale admin console under Machines -> Edit route settings before expecting other tailnet devices to reach your LAN.
Configuring exit nodes
An exit node sends all traffic from one tailnet device through another.
This is useful when you want your phone or laptop to route through your home network while away.
Enable IP forwarding on the Docker host first:
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' | sudo tee /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
On Orange Pi / Armbian boards, you may prefer /etc/sysctl.conf:
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p /etc/sysctl.conf
Then set the compose environment to advertise the exit node and recreate the container:
TS_EXTRA_ARGS=--advertise-exit-node
docker compose up -d --force-recreate
Approve the exit-node route in the Tailscale admin console.
After that, other tailnet devices can select this machine as their exit node from the Tailscale client.
Headscale: self-hosted control plane
Tailscale’s clients are open source, but the hosted coordination server is not. Headscale is a community-built, Apache 2.0 licensed control plane that speaks the same protocol. You run it on your own server, point your Tailscale clients at it, and keep the VPN coordination layer under your roof.
It’s the right move when:
- You want the coordination server and its metadata under your control.
- You are comfortable operating identity, ACLs, upgrades, backups, and client enrollment yourself.
- You are building something that depends on Tailscale-compatible clients and want to reduce SaaS lock-in.
For most home labs, vanilla Tailscale is plenty. Headscale is the upgrade path when you deliberately want to own the control plane.
Tailscale vs WireGuard vs other VPNs
| Tool | Control plane | Setup difficulty | Best for |
|---|---|---|---|
| WireGuard | None, you manage configs yourself | High | Static, small networks |
| Tailscale | Tailscale SaaS | Low | Most home labs |
| Headscale | Self-hosted | Medium | Privacy-focused or independent tailnets |
| Gluetun | N/A, outbound VPN client | Low | Routing containers through commercial VPNs |
| Pangolin | Self-hosted | Medium | Exposing services without port forwarding |
Conclusion
Tailscale is the shortest path from “I need a VPN” to a working mesh.
Drop it into Docker, advertise your subnet, and every service in your lab is reachable from your phone, laptop, and any other tailnet device.
When you outgrow the hosted coordination layer, Headscale is waiting. New to Docker? Start with the containers primer, then put the rest of your services behind Nginx Proxy Manager once they are reachable privately.
Project Links
FAQ
Is Tailscale really free?
Does Tailscale traffic actually go through Tailscale's servers?
How do I avoid NAT issues without Tailscale?
What is the difference between Tailscale and Headscale?
Why does the Docker container need extra network permissions?
NET_ADMIN, NET_RAW, access to /dev/net/tun, and network_mode: host so the container can operate as a real network node on the host.
Can I run Tailscale alongside Docker bridge networks?
network_mode: host to advertise the host or LAN to the tailnet. Combined with subnet routes, you can reach services without putting every application container directly on the tailnet.
Comments