Tailscale is a modern VPN built on WireGuard with automatic mesh networking. For complete self-hosting, use Headscale—an open-source Tailscale control server implementation.
UniFi Devices: If you run UniFi hardware (Dream Machine, Dream Router, etc.), install Tailscale directly on your gateway:
1
2
3
4
5
6
7
8
| # Install the latest version of Tailscale on UniFi OS 2.x+
curl -sSLq https://raw.github.com/SierraSoftworks/tailscale-udm/main/install.sh | sh
# Start Tailscale
tailscale up
# Advertise routes to your local network
tailscale up --advertise-routes=10.0.0.0/24 --advertise-exit-node
|
The installation automatically restarts after UniFi updates, runs via systemd on UniFi OS 2.x+, and supports Tailscale SSH. It works on UniFi Dream Machine, Dream Router, Cloud Key Gen 2, and similar devices.
Manage the service with:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Restart Tailscale
systemctl restart tailscaled
# Upgrade Tailscale
apt update && apt install -y tailscale
# Or via helper script
/data/tailscale/manage.sh update
# Enable Tailscale SSH
tailscale up --ssh
# Generate HTTPS certificates
/data/tailscale/manage.sh cert generate
/data/tailscale/manage.sh cert install-unifi
|
For full documentation and advanced configuration, see the tailscale-udm GitHub repository.
Option 2: Self-Hosted Headscale#
For complete control and privacy, deploy Headscale - a self-hosted Tailscale control server.
Docker Compose Setup:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| services:
headscale:
container_name: headscale
image: headscale/headscale:v0.26.0
restart: unless-stopped
ports:
- '8080:8080'
- '9090:9090'
volumes:
- '/data/docker/headscale/config:/etc/headscale'
- '/data/docker/headscale/data:/var/lib/headscale'
environment:
TZ: 'America/Montreal'
command: serve
headplane:
container_name: headplane
image: ghcr.io/tale/headplane:0.6.0
restart: unless-stopped
ports:
- '3000:3000'
volumes:
- '/data/docker/headplane/config/config.yaml:/etc/headplane/config.yaml'
- '/data/docker/headscale/config/config.yaml:/etc/headscale/config.yaml'
- '/data/docker/headplane/data:/var/lib/headplane'
- '/var/run/docker.sock:/var/run/docker.sock:ro'
|
Key Configuration (config.yaml):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| server_url: https://hs.yourdomain.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
database:
sqlite:
write_ahead_log: true
policy:
mode: database # Allows ACL management via Headplane UI
dns:
magic_dns: true
base_domain: ts.yourdomain.com
override_local_dns: true
nameservers:
global:
- 192.168.1.1 # Your DNS server (e.g., Pi-hole)
|
Headplane Configuration (/etc/headplane/config.yaml):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| server:
host: "0.0.0.0"
port: 3000
cookie_secret: "your-exactly-32characters-long"
cookie_secure: true
headscale:
url: "https://hs.yourdomain.com"
public_url: "https://hs.yourdomain.com"
config_path: "/etc/headscale/config.yaml"
integration:
docker:
enabled: true
container_name: "headscale"
socket: "unix:///var/run/docker.sock"
|
Create API Key:
1
| docker exec headscale headscale apikeys create --expiration 999d
|
Add Users:
1
| docker exec headscale headscale users create USERNAME
|
Access Control via ACL:
Create granular access rules using tags:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| {
"groups": {
"group:admin": ["user@"]
},
"tagOwners": {
"tag:me": ["group:admin"],
"tag:private": ["group:admin"],
"tag:user": ["group:admin"]
},
"acls": [
{
"action": "accept",
"src": ["tag:me"],
"dst": [
"tag:private:*",
"autogroup:internet:*",
"192.168.1.0/24:*"
]
},
{
"action": "accept",
"src": ["tag:user"],
"dst": [
"100.64.0.10:9000", # Specific service access only
"100.64.0.11:8000"
]
}
]
}
|
Reverse Proxy Setup#
You need to expose Headscale via HTTPS. Example Caddy configuration:
1
2
3
4
5
6
7
8
9
10
11
| hs.yourdomain.com {
# Restrict admin interface to local network
@admin_allowed {
path /admin /admin/*
remote_ip 192.168.1.0/24 100.64.0.0/24
}
reverse_proxy @admin_allowed localhost:3000
# Main Headscale server
reverse_proxy localhost:8080
}
|
Resources: