Most developers and homelab enthusiasts want secure, zero-config access to their home network from anywhere—without exposing ports or struggling with dynamic DNS. Tailscale offers this using WireGuard and NAT traversal, but for privacy, control, or cost reasons, you might prefer to self-host your coordination server.
This post walks through:
- Deploying Headscale (the open-source Tailscale coordination server) in Docker on AWS EC2
- Connecting devices (Mac, Windows, iOS, Raspberry Pi, etc.)
- Accessing your home LAN securely by advertising and enabling subnet routes
Why Tailscale?
Tailscale creates a secure, encrypted mesh network between your devices. Each device gets a unique IP in the 100.x.y.z range and can directly communicate with others, bypassing NAT, firewalls, and even CG-NAT. Key benefits:
- Zero port forwarding
- Single sign-on and ACLs
- Cross-platform clients
- MagicDNS for easy name resolution
- Optional exit nodes and subnet routes
However, using Tailscale.com means trusting a third-party control plane. That’s where Headscale comes in.
(1) Docker-Based Headscale on EC2
Assuming you’re comfortable with SSH, Docker, and DNS:
Prerequisites
- EC2 instance (Ubuntu 22.04 or 24.04 preferred)
- Domain/subdomain pointing to your instance (e.g., headscale.example.com)
- Docker and Docker Compose
- Basic UFW firewall rules allowing ports 80, 443, 3478/udp, and 41641/udp
Docker Compose Example
services: headscale: image: headscale/headscale:latest container_name: headscale restart: always ports: - "8080:8080" # Headscale web API volumes: - ./config:/etc/headscale - ./data:/var/lib/headscale command: headscale serve
Above, also assumes you know how to reverse proxy tailscale. Such as caddy.
Initialize Headscale:
docker exec headscale headscale users create <user> docker exec headscale headscale preauthkeys create --reusable --expiration 24h
<user> is the user you’re creating.
Use the output URL (or tailscale up –login-server) to connect clients.
(2) Installing Clients on Devices
macOS / Linux / Windows
Install the official Tailscale client, then run:
tailscale up --login-server https://headscale.example.com
iOS / Android
Install the app, then visit the /register URL from Headscale to authenticate. You can also generate a reusable key with –ephemeral for IoT-style devices.
Raspberry Pi
Install via:
curl -fsSL https://tailscale.com/install.sh | sh sudo tailscale up --login-server https://headscale.example.com \ --advertise-routes=192.168.0.0/24 \ --accept-routes
This advertises your home subnet to the Tailscale network.
(3) Enabling Subnet Routes to Your LAN
After advertising the subnet, you’ll need to enable it manually via Headscale:
docker exec headscale headscale routes list # Note the ID for the route, e.g., 1 docker exec headscale headscale routes enable --route 1
Now all devices on your Tailscale network can access your local network (e.g., 192.168.0.x) via your Pi.
Make sure IP forwarding is enabled and iptables/nftables allows routing:
echo 1 > /proc/sys/net/ipv4/ip_forward
Use a systemd script or rc.local to persist this change across reboots.
Final Thoughts
Using Tailscale with Headscale gives you:
- Private WireGuard-based networking
- Access to all your devices, including those behind CG-NAT
- Control over coordination with no third-party dependency
- Subnet routing, acting like a VPN into your LAN
You maintain complete control of authentication, ACLs, and key expiration—ideal for those who want privacy and flexibility.
Whether you’re backing up photos from your phone to your NAS, administering a Docker swarm, or checking on a security cam, this is one of the cleanest ways to securely bridge multiple networks.
Leave a Reply