Tutoriais de VPS
Montar Headscale em VPS (2026): servidor de controle Tailscale auto-hospedado, HTTPS, políticas de acesso e roteamento de sub-redes
Monte em um VPS um servidor de controle Headscale auto-hospedado para Tailscale: HTTPS com Caddy, registro de dispositivos, políticas Grants, roteamento de sub-redes, Exit Node, DERP, backup, atualização e solução de problemas.

Montar Headscale em VPS (2026): servidor de controle Tailscale auto-hospedado, HTTPS, políticas de acesso e roteamento de sub-redes
Se você quer montar uma rede com o Tailscale mas não quer entregar o gerenciamento de dispositivos, políticas e nós a um plano de controle hospedado, o Headscale é basicamente a opção da qual não dá para fugir.
Mas primeiro vamos esclarecer um equívoco: o Headscale não é um "serviço de VPN em que todo o tráfego passa pelo VPS". Ele cuida do registro de dispositivos, da distribuição de chaves, das políticas e da coordenação de rotas; quando dois dispositivos conseguem se conectar diretamente, os dados reais continuam trafegando por WireGuard criptografado ponto a ponto. Só quando a conexão direta falha e o relay DERP está habilitado é que o tráfego pode passar por um servidor de relay.
Dá para dividir em duas camadas:
- Headscale: o plano de controle que você mesmo mantém, responsável por decidir "quem pode entrar, quem pode acessar quem";
- Cliente Tailscale: instalado em computadores, celulares e servidores, recebendo a configuração do plano de controle;
- WireGuard: o plano de dados que de fato transporta o tráfego criptografado entre os clientes.
Se você só precisa interligar três ou cinco máquinas fixas, o tutorial de WireGuard em VPS é mais simples. O Headscale é mais indicado para home labs e equipes pequenas com mais dispositivos que precisam de usuários e tags, roteamento de sub-redes ou políticas unificadas.
O custo também é claro: quando o servidor de controle cai, as conexões existentes não necessariamente caem na hora, mas o registro de novos dispositivos, as atualizações de políticas e a recoordenação de nós serão afetados. Atualizações, backups e renovação do domínio ficam por sua conta.
O Headscale em si não consome muitos recursos; 1 núcleo, 1 GB de RAM e 10–20 GB de SSD é um ponto de partida razoável. Se você habilitar o DERP integrado, a largura de banda de relay pode virar o gargalo antes da CPU e da memória, então vale acompanhar com monitoramento de tráfego de VPS e alertas de excesso.
Prepare um domínio dedicado, por exemplo headscale.example.com, com um registro A apontando diretamente para o IP público do VPS.
Se o DNS estiver hospedado na Cloudflare, você precisa usar a nuvem cinza "somente DNS". O protocolo de controle do Headscale faz upgrade de conexões via POST, e o proxy de nuvem laranja da Cloudflare e o Cloudflare Tunnel não suportam esse caminho.
Portas necessárias:
| Porta | Uso |
|---|---|
| TCP 22 | Gerenciamento SSH, de preferência restrito por origem |
| TCP 80/443 | Emissão de certificado do Caddy e HTTPS |
| UDP 3478 | STUN apenas para DERP integrado |
Primeiro configure o SSH e o firewall conforme a lista de reforço de segurança para VPS, e não se tranque do lado de fora ao alterar o UFW.
O Headscale atualmente recomenda o DEB oficial para Ubuntu 22.04+ ou Debian 12+. No momento da verificação deste artigo, a versão estável era a v0.29.2, mas na hora de instalar você ainda deve ler a versão estável mais recente pela API do GitHub:
sudo apt update
sudo apt install -y curl jq ca-certificates
headscale_version="$(curl -fsSL https://api.github.com/repos/juanfont/headscale/releases/latest | jq -r '.tag_name | ltrimstr("v")')"
headscale_arch="$(dpkg --print-architecture)"
printf 'version=%s arch=%s\n' "$headscale_version" "$headscale_arch"
curl -fL \
-o /tmp/headscale.deb \
"https://github.com/juanfont/headscale/releases/download/v${headscale_version}/headscale_${headscale_version}_linux_${headscale_arch}.deb"
sudo apt install /tmp/headscale.deb
O pacote oficial cria um usuário dedicado, uma configuração de exemplo e um serviço systemd. Não abra as portas com pressa; faça backup da configuração atual e compare com o exemplo completo da versão atual:
sudo cp /etc/headscale/config.yaml /etc/headscale/config.yaml.before-guide
sudo less /usr/share/doc/headscale/examples/config-example.yaml
sudoedit /etc/headscale/config.yaml
Não copie um config.yaml inteiro de um blog antigo. Os campos mudam entre versões; o mais seguro é modificar estas chaves no exemplo incluído no pacote:
server_url: https://headscale.example.com
listen_addr: 127.0.0.1:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 127.0.0.1:50443
trusted_proxies:
- 127.0.0.1/32
- ::1/128
database:
type: sqlite
sqlite:
path: /var/lib/headscale/db.sqlite
write_ahead_log: true
policy:
mode: file
path: /etc/headscale/policy.hujson
dns:
magic_dns: true
base_domain: tail.example.net
dns.base_domain não pode usar o mesmo domínio que server_url. Mantenha listen_addr no endereço de loopback e exponha apenas o Caddy para a internet.
Valide a configuração antes de iniciar:
sudo headscale configtest
sudo systemctl enable --now headscale
sudo systemctl status headscale --no-pager
Para o processo completo de instalação do Caddy, consulte o guia de proxy reverso Caddy em VPS. Após instalar, edite /etc/caddy/Caddyfile:
http://headscale.example.com {
handle /generate_204 {
respond 204
}
handle * {
redir https://{host}{uri}
}
}
headscale.example.com {
reverse_proxy 127.0.0.1:8080 {
header_up True-Client-IP {remote_host}
header_up X-Real-IP {remote_host}
}
}
Verifique e recarregue:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
curl -fsS https://headscale.example.com/health
Depois que o endpoint de saúde funcionar, confirme que a porta 8080 não está escutando na internet:
sudo ss -lntup | grep -E ':443|:8080|:3478'
Crie um usuário no VPS do Headscale:
sudo headscale users create alice
sudo headscale users list
Instale o cliente oficial do Tailscale no cliente e aponte-o para o servidor de controle personalizado:
sudo tailscale up --login-server=https://headscale.example.com
O navegador mostrará as instruções de registro e um Auth ID. Volte ao VPS do Headscale para aprovar:
sudo headscale auth register --user alice --auth-id AUTH_ID
sudo headscale nodes list
Para integração automatizada, você pode gerar uma chave de pré-autenticação de uso único:
sudo headscale preauthkeys create --user USER_ID
sudo tailscale up \
--login-server=https://headscale.example.com \
--authkey AUTH_KEY
Por padrão, a chave só pode ser usada uma vez e é válida por uma hora. Ela equivale a uma credencial temporária; não a escreva em imagens, repositórios ou no histórico do shell.
Quando não for possível executar comandos diretamente no celular, escolha usar outro servidor no menu de conta do app Tailscale e informe o https://headscale.example.com completo. O nome da entrada pode variar um pouco entre versões do cliente, mas o endereço do servidor de controle deve coincidir exatamente com server_url. Após o registro, verifique imediatamente no servidor o nome do nó, o usuário e a última vez online; para dispositivos retirados ou perdidos, confirme o identificador na lista de nós antes de excluir, e não se limite a editar Grants em vez de revogar o dispositivo.
Quando nenhuma política está carregada, os nós do Headscale podem se acessar por padrão. Primeiro crie um /etc/headscale/policy.hujson que negue tudo:
{
"grants": []
}
Depois de confirmar que a política carregou, adicione permissões conforme necessário. Por exemplo, permitir que os dispositivos pessoais da Alice se comuniquem entre si e que ela acesse as portas 22 e 443 dos servidores com a tag tag:server:
{
"tagOwners": {
"tag:server": ["alice@"]
},
"grants": [
{
"src": ["alice@"],
"dst": ["autogroup:self"],
"ip": ["*"]
},
{
"src": ["alice@"],
"dst": ["tag:server"],
"ip": ["22,443"]
}
]
}
Recarregue e veja os logs de análise:
sudo systemctl reload headscale
sudo journalctl -u headscale -n 100 --no-pager
Quando uma política estiver escrita errada, não reinicie repetidamente. Restaure primeiro a versão anterior do arquivo, confirme que os logs não mostram erros de análise e só então continue editando.
O roteamento de sub-redes permite que dispositivos do Tailnet acessem dispositivos da LAN que não conseguem rodar o Tailscale; um Exit Node encaminha o tráfego de internet do cliente por um nó designado. Ambos exigem que o nó de encaminhamento os anuncie ativamente e que o Headscale os aprove.
No nó de encaminhamento, primeiro habilite o encaminhamento de IP:
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' |
sudo tee /etc/sysctl.d/99-tailscale.conf
sudo sysctl --system
Anuncie a rota da LAN e aprove:
sudo tailscale set --advertise-routes=192.168.50.0/24
sudo headscale nodes list-routes
sudo headscale nodes approve-routes \
--identifier NODE_ID \
--routes 192.168.50.0/24
O cliente aceita a rota:
sudo tailscale set --accept-routes
O processo do Exit Node é semelhante:
sudo tailscale set --advertise-exit-node
sudo headscale nodes list-routes
sudo headscale nodes approve-routes \
--identifier NODE_ID \
--routes 0.0.0.0/0
sudo tailscale set --exit-node EXIT_NODE_NAME
Aprovar uma rota significa apenas que ela pode ser usada; os Grants ainda devem restringir quais usuários podem acessar a sub-rede correspondente ou a internet.
Os dispositivos tentam primeiro a conexão direta UDP. Só atrás de NAT simétrico, firewalls rígidos ou redes de operadora complexas é que podem recorrer ao DERP.
O Headscale não habilita o DERP integrado por padrão e continua fornecendo aos clientes a lista pública de DERP do Tailscale. Se você realmente quiser auto-hospedar um relay, habilite-o em derp.server de config.yaml, mantenha verify_clients: true, preencha o IP público real do VPS e abra a UDP 3478.
Não esvazie de cara o DERP público urls. Deixar apenas um DERP auto-hospedado cria um ponto único de falha, e a velocidade do relay é limitada pela largura de banda e pela rota do VPS.
Verifique a conexão:
tailscale ping PEER_NAME
tailscale netcheck
tailscale debug derp-map
tailscale debug derp headscale
tailscale ping mostrando direct significa conexão ponto a ponto; mostrar via DERP não significa que a criptografia falhou, apenas que o tráfego dá um salto extra.
curl -Iv https://headscale.example.com/health
sudo journalctl -u headscale -n 200 --no-pager
sudo journalctl -u caddy -n 100 --no-pager
Verifique principalmente se o domínio ainda está com a nuvem laranja da Cloudflare, se a hora do sistema está correta, se o certificado do Caddy foi emitido com sucesso e se server_url está configurado com o endereço HTTPS final.
sudo headscale nodes list
tailscale status
sudo systemctl status tailscaled --no-pager
Se o lado do controle está online mas o cliente está offline, provavelmente é um problema do serviço do cliente, DNS, proxy ou firewall; continue investigando com a lista de verificação de resolução DNS em VPS.
Primeiro veja se o Headscale carregou os Grants com sucesso e depois confira usuários, tags, portas de destino e se as rotas foram aprovadas. Não volte a política para allow-all permanente só para "fazer funcionar primeiro".
O essencial para fazer backup é o SQLite, as chaves privadas Noise/DERP, a configuração, as políticas e o Caddyfile. A forma simples mais confiável é parar brevemente o serviço de controle e depois empacotar:
backup_date="$(date +%F)"
sudo systemctl stop headscale
sudo tar -C / -czf "/root/headscale-backup-${backup_date}.tar.gz" \
etc/headscale \
var/lib/headscale \
etc/caddy/Caddyfile
sudo systemctl start headscale
sudo tar -tzf "/root/headscale-backup-${backup_date}.tar.gz" | head
curl -fsS https://headscale.example.com/health
sudo headscale nodes list
Isso causa uma breve interrupção do plano de controle; as conexões diretas existentes geralmente continuam, mas não faça isso durante registro em massa de dispositivos. Antes de uma atualização formal, faça outro simulado de backup e restauração em VPS.
Ordem de atualização:
- Anote o
headscale versionatual; - Faça o backup e verifique o arquivo compactado;
- Baixe o novo DEB oficial;
- Compare com o
config-example.yamlda nova versão; - Execute
headscale configtest; - Após reiniciar, verifique o health, a lista de nós, os Grants, as rotas e um peer ping real.
Se a atualização falhar, instale o DEB da versão original e restaure o banco de dados e a configuração do mesmo backup; não continue tentando várias versões sobre um estado quebrado.
O health da web retornar sucesso só prova que o serviço de controle está acessível, não que toda a rede privada já funciona. A partir de pelo menos uma rede externa, faça as seguintes verificações:
- Um novo dispositivo consegue se registrar via HTTPS e pertence ao usuário correto em
headscale nodes list; - Dois dispositivos autorizados a se acessar conseguem resolver por nome de host e conectar, e as portas não autorizadas são de fato negadas;
tailscale pingconsegue distinguir conexão direta de DERP, e as transferências se completam de forma estável mesmo com relay;- Um cliente com roteamento de sub-redes consegue acessar o endereço da LAN de destino, e o acesso desaparece ao desativar
--accept-routes; - Se o Exit Node estiver habilitado, o IP de saída muda ao acessar a internet e volta à saída local ao desmarcá-lo;
- Após reiniciar o Headscale e o Caddy, os nós, as políticas e as rotas continuam existindo;
- Descompacte o backup em outra máquina e confirme que o banco de dados, as políticas e os arquivos de chave privada estão realmente incluídos.
Por fim, configure monitoramento para a renovação do certificado do domínio, o espaço em disco e anomalias do serviço. O plano de controle costuma ficar silencioso; o que é realmente perigoso são certificados vencidos, disco cheio e backups irrecuperáveis que passam muito tempo sem ninguém notar.
- Quer tranquilidade e tem poucos dispositivos: use diretamente o plano de controle hospedado do Tailscale;
- Quer controlar você mesmo os dispositivos e as políticas e consegue assumir a manutenção: use o Headscale;
- Tem apenas alguns nós fixos e não precisa de gerenciamento de usuários, tags e rotas: use o WireGuard nativo.
O verdadeiro valor do Headscale não é ser um "Tailscale grátis", mas sim o controle. Só depois de também gerenciar DNS, Grants, backups e atualizações é que esse controle deixa de se tornar um novo ponto único de falha.