Gerador de configuração Headscale
Indique o seu domínio, escolha Docker ou o pacote .deb e como o HTTPS é tratado: o gerador escreve o config.yaml, o ficheiro compose ou o script de instalação, o ficheiro do proxy reverso, uma política opcional de exit node e os comandos para criar o primeiro utilizador e ligar um dispositivo. Feito a partir dos ficheiros oficiais do Headscale 0.29.5. Nada sai do seu navegador.
Tudo é gerado no seu navegador. Nada é enviado nem guardado.
Um nome DNS cujo registo A (e AAAA) aponta para o VPS, por exemplo headscale.example.com.
Os dispositivos passam a ser nome.dominio-base. Tem de ser diferente do domínio acima; não precisa de existir publicamente.
Criado com headscale users create. Também é o dono da tag na política de exit node.
Usado pelo certbot ou pelo Headscale para avisos de expiração. O Caddy funciona sem ele.
O Caddy é o caminho mais curto. Escolha o nginx se já corre no servidor, o TLS integrado se o VPS só corre o Headscale.
- Crie o registo DNS do domínio antes de arrancar: o pedido de certificado falha se o nome ainda não apontar para o VPS.
Gerado para https://headscale.example.com · Headscale 0.29.5 · 6 ficheiros
---
# Headscale 0.29.5: /etc/headscale/config.yaml
server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090
grpc_listen_addr: 127.0.0.1:50443
grpc_allow_insecure: false
trusted_proxies: []
noise:
private_key_path: /var/lib/headscale/noise_private.key
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
allocation: sequential
derp:
server:
enabled: false
region_id: 999
region_code: "headscale"
region_name: "Headscale Embedded DERP"
verify_clients: true
stun_listen_addr: "0.0.0.0:3478"
private_key_path: /var/lib/headscale/derp_server_private.key
automatically_add_embedded_derp_region: true
urls:
- https://controlplane.tailscale.com/derpmap/default
paths: []
auto_update_enabled: true
update_frequency: 3h
disable_check_updates: false
node:
expiry: 0
ephemeral:
inactivity_timeout: 30m
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
write_ahead_log: true
wal_autocheckpoint: 1000
acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: ""
tls_letsencrypt_hostname: ""
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
tls_letsencrypt_challenge_type: HTTP-01
tls_letsencrypt_listen: ":http"
tls_cert_path: ""
tls_key_path: ""
log:
level: info
format: text
policy:
mode: file
path: "/etc/headscale/policy.json"
dns:
magic_dns: true
base_domain: ts.example.com
override_local_dns: true
nameservers:
global:
- 1.1.1.1
- 1.0.0.1
- 2606:4700:4700::1111
- 2606:4700:4700::1001
split: {}
search_domains: []
extra_records: []
unix_socket: /var/run/headscale/headscale.sock
unix_socket_permission: "0770"
logtail:
enabled: false
taildrop:
enabled: true
services:
headscale:
image: docker.io/headscale/headscale:0.29.5
container_name: headscale
restart: unless-stopped
read_only: true
tmpfs:
- /var/run/headscale
ports:
- "127.0.0.1:8080:8080"
- "127.0.0.1:9090:9090"
volumes:
- /opt/headscale/config:/etc/headscale:ro
- /opt/headscale/lib:/var/lib/headscale
command: serve
healthcheck:
test: ["CMD", "headscale", "health"]
caddy:
image: docker.io/library/caddy:2
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- /opt/headscale/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- headscale
volumes:
caddy_data:
caddy_config:
http://headscale.example.com {
# Tailscale captive portal detection
handle /generate_204 {
respond 204
}
handle * {
redir https://{host}{uri}
}
}
headscale.example.com {
reverse_proxy headscale:8080 {
header_up True-Client-IP {remote_host}
header_up X-Real-IP {remote_host}
}
}
{
"tagOwners": {
"tag:exit": [
"admin@"
]
},
"autoApprovers": {
"exitNode": [
"tag:exit"
]
}
}
#!/usr/bin/env bash
set -euo pipefail
# 1. Instalar o Docker se faltar (script oficial)
command -v docker >/dev/null || curl -fsSL https://get.docker.com | sudo sh
# 2. Pastas para a configuração e os dados
sudo mkdir -p /opt/headscale/config /opt/headscale/lib
cd /opt/headscale
# 3. Guarde primeiro os ficheiros gerados nestes caminhos
for f in config/config.yaml docker-compose.yaml Caddyfile config/policy.json; do
[ -f "$f" ] || { echo "Missing /opt/headscale/$f"; exit 1; }
done
# 4. Arrancar o Headscale
sudo docker compose up -d
sleep 5
# 5. Verificação: deve mostrar um estado de saúde em JSON
curl -fsS http://127.0.0.1:8080/health
# Criar o primeiro utilizador
sudo docker exec headscale headscale users create admin
# Anotar o ID numérico do utilizador
sudo docker exec headscale headscale users list
# Criar uma chave pré-autenticada (vale 1 hora, uso único por omissão)
sudo docker exec headscale headscale preauthkeys create --user <USER_ID>
# Em cada dispositivo: autenticar-se no seu servidor Headscale
sudo tailscale up --login-server https://headscale.example.com --authkey <YOUR_AUTH_KEY>
# No VPS exit node: autenticar-se com a tag para a aprovação automática
sudo tailscale up --login-server https://headscale.example.com --authkey <YOUR_AUTH_KEY> \
--advertise-exit-node --advertise-tags=tag:exit
# Depois de editar o policy.json: recarregar o Headscale
sudo docker kill --signal=HUP headscale
# Autenticação pelo navegador: aprovar o dispositivo com o Auth ID que mostra
sudo docker exec headscale headscale auth register --user admin --auth-id <AUTH_ID>
# Listar os dispositivos ligados
sudo docker exec headscale headscale nodes list
O Headscale precisa de um servidor sempre ligado
Um VPS pequeno com IPv4 e IPv6 públicos corre estes ficheiros tal como estão, e a mesma máquina pode ser o exit node da sua tailnet.
Link de afiliado: podemos receber uma comissão, sem custo extra para si. O gerador continua gratuito e não depende disso.
O que fazem os ficheiros gerados
O Headscale é o substituto open source do servidor de controlo da Tailscale: os seus dispositivos continuam a usar as apps oficiais da Tailscale, mas autenticam-se no seu próprio servidor com --login-server. Precisa de três coisas: um endereço HTTPS público na porta 443 igual ao server_url, um base_domain MagicDNS diferente desse endereço e um proxy reverso que deixe passar os upgrades WebSocket se o Headscale não tratar ele próprio do TLS.
O config.yaml é o ficheiro de exemplo completo da versão 0.29.5 com os seus valores: todas as chaves estão presentes e nada depende de um valor por omissão não documentado. Em Docker, o contentor escuta em 0.0.0.0 na sua própria rede e as portas só são publicadas em 127.0.0.1, exceto 80 e 443 quando é o Headscale a terminar o TLS. Atrás de um proxy reverso na mesma máquina, trusted_proxies aponta para localhost para que os IP reais dos clientes apareçam nos registos.
Vários comandos mudaram nas últimas versões, e é por isso que muitos tutoriais falham: as chaves pré-autenticadas criam-se agora com o ID numérico do utilizador (mostrado por headscale users list), e um dispositivo que se autentica pelo navegador é aprovado com headscale auth register e o seu Auth ID. O bloco dos primeiros passos usa a sintaxe 0.29.
Caddy, nginx ou TLS integrado?
Três formas de servir o Headscale em HTTPS, todas documentadas pelo projeto. A escolha certa depende do que já corre no servidor.
| HTTPS tratado por | Certificado | Ideal quando | Atenção |
|---|---|---|---|
| Caddy (proxy reverso) | Automático, renovado pelo Caddy | Quer a instalação mais curta ou outros sites no mesmo VPS | As portas 80 e 443 têm de chegar ao Caddy; o WebSocket funciona sem linhas extra |
| nginx (proxy reverso) | certbot, renovado pelo seu temporizador | O nginx já serve outros sites na máquina | Os cabeçalhos Upgrade e Connection são obrigatórios, senão os clientes nunca ligam |
| Let's Encrypt integrado no Headscale | Automático, renovado pelo Headscale | O VPS só corre o Headscale | O Headscale ocupa as portas 80 e 443; nenhum outro serviço web nesse IP |