SSD Nodes Learn 8GB de RAM — $66/ano
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-02

Como hospedar seu próprio servidor Tailscale com headscale

Instale o headscale em um VPS pelo .deb oficial, defina server_url antes de iniciar o serviço e conecte o primeiro nó à sua rede privada.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

O que é o headscale

O headscale é uma implementação auto-hospedada do servidor de controle do Tailscale. Assim, a máquina que coordena sua rede privada é um VPS que você possui. É um projeto da comunidade e não é operado pela Tailscale Inc. Cada máquina continua executando o cliente oficial tailscale, apontado para o seu servidor com uma flag, --login-server.

O servidor de controle é a parte que sabe quem pertence à rede. Ele atribui a cada nó um endereço do intervalo 100.64.0.0/10, distribui chaves públicas e informa aos nós onde encontrar uns aos outros. Os túneis continuam usando WireGuard e são estabelecidos de nó para nó. O tráfego entre duas das suas máquinas não passa pelo servidor headscale, a menos que não seja possível criar um caminho direto e os nós usem um relay.

Cada instância do headscale atende a um tailnet (uma rede Tailscale), o que o projeto considera adequado para uso pessoal ou para uma organização pequena. Com três ou quatro máquinas, uma VPN WireGuard simples em um VPS que você possui requer menos software para manter e oferece menos pontos de falha. O headscale se torna útil quando você não quer mais escrever manualmente um bloco [Peer] para cada novo laptop. Para uma comparação mais ampla dos dois modelos, consulte as diferenças entre WireGuard e Tailscale.

O que você precisa antes de instalar

  • Um VPS executando Ubuntu 24.04 com um endereço IPv4 público e acesso sudo. Se o servidor for novo, siga primeiro os primeiros dez minutos em um VPS novo.
  • Um registro DNS A apontando para esse endereço. Este guia usa headscale.example.com.
  • Um segundo domínio ou subdomínio para o MagicDNS. Este guia usa tailnet.example.net. Ele não pode ser o mesmo domínio usado em server_url.
  • Uma máquina cliente para ingressar, executando Linux, macOS, Windows, Android ou iOS.

Instalar o headscale a partir do .deb oficial

O projeto publica pacotes .deb na página de releases do GitHub. Em julho de 2026, a versão atual é 0.29.3. Verifique primeiro a arquitetura, porque o nome do arquivo inclui essa informação.

sudo apt update
sudo apt install -y wget
dpkg --print-architecture

Esse comando exibe amd64 em uma VPS x86 comum e arm64 em um plano do tipo Ampere ou Graviton. Coloque a resposta na variável abaixo.

HEADSCALE_VERSION="0.29.3"
HEADSCALE_ARCH="amd64"
wget --output-document=headscale.deb \\
  "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
sudo apt install -y ./headscale.deb
headscale version

O ./ antes do nome do arquivo é obrigatório. Sem ele, apt procura nos repositórios um pacote chamado headscale.deb e falha.

O pacote cria um usuário de sistema headscale, grava um /etc/headscale/config.yaml padrão e instala uma unidade do systemd. Ele não inicia o serviço, e essa é a ordem correta. A configuração fornecida aponta server_url para http://127.0.0.1:8080, que não é um endereço que nenhum cliente seu possa alcançar. Portanto, iniciar o serviço agora estaria incorreto, mesmo que ele fosse iniciado. Executar sudo systemctl is-active headscale neste momento exibe inactive. Isso é esperado, não um erro.

Configure server_url antes de iniciar o serviço

Edite /etc/headscale/config.yaml com sudo nano /etc/headscale/config.yaml ou aplique as mesmas três alterações com sed. Mantenha uma cópia do original, porque o arquivo é longo e contém muitos comentários. Ele é a melhor referência disponível para as demais configurações.

sudo cp /etc/headscale/config.yaml /etc/headscale/config.yaml.orig
sudo sed -i 's|^server_url:.*|server_url: https://headscale.example.com|' /etc/headscale/config.yaml
sudo sed -i 's|^listen_addr:.*|listen_addr: 127.0.0.1:8080|' /etc/headscale/config.yaml
sudo sed -i 's|^  base_domain:.*|  base_domain: tailnet.example.net|' /etc/headscale/config.yaml
sudo grep -E '^(server_url|listen_addr):|^  base_domain:' /etc/headscale/config.yaml

server_url é o endereço que o headscale grava em cada registro de cliente. Depois disso, os clientes sempre se conectam exatamente a essa string. Portanto, ela deve ser o nome público com https:// na frente, nunca 127.0.0.1.

listen_addr define onde o processo aceita conexões. Mantenha-o no loopback. Um proxy reverso no mesmo host termina o TLS (transport layer security) e encaminha as solicitações para ele. Assim, nada fora do servidor precisa acessar a porta 8080.

base_domain é o sufixo do MagicDNS, o domínio sob o qual os nós recebem nomes. Ele deve ser um nome de domínio totalmente qualificado, sem ponto final, e deve ser diferente do domínio definido em server_url, porque, caso contrário, os dois espaços de nomes entrariam em conflito.

Não altere a seção do banco de dados. O padrão é SQLite em /var/lib/headscale/db.sqlite, em um diretório criado e pertencente ao pacote. O SQLite é suficiente para uma tailnet desse tamanho.

Inicie o headscale e confirme que ele está em execução

sudo systemctl enable --now headscale
sudo systemctl is-active headscale
curl -sS -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:8080/health

is-active exibe active, e curl exibe 200. enable --now executa as duas partes: inicia o serviço e configura sua inicialização após uma reinicialização.

Se is-active exibir failed, leia o journal com sudo journalctl -u headscale -n 50 --no-pager. Uma falha nesta etapa quase sempre está no arquivo de configuração, porque o headscale analisa o arquivo inteiro antes de abrir um socket. Portanto, uma indentação incorreta ou uma chave desconhecida interrompe o processo antes que qualquer coisa fique escutando. Corrija o arquivo e execute sudo systemctl restart headscale. Toda alteração posterior na configuração exige a mesma reinicialização. Os clientes se reconectam automaticamente depois. Se as unidades do systemd forem novas para você, executar seus próprios serviços e timers com systemd apresenta os comandos usados aqui.

Verifique os arquivos de estado enquanto estiver no shell:

stat -c '%U %n' /var/lib/headscale/db.sqlite /var/lib/headscale/noise_private.key

As duas linhas começam com headscale, o usuário sem privilégios criado pelo pacote. noise_private.key é a identidade do servidor para os clientes. Mantenha esse arquivo. Se você o excluir, o headscale gerará uma nova identidade, e todos os nós terão que se registrar novamente.

Coloque TLS na frente do headscale

Os clientes devem acessar server_url por HTTPS. O Caddy é o caminho mais curto, porque solicita e renova o certificado automaticamente.

sudo apt install -y caddy

Substitua /etc/caddy/Caddyfile pelo bloco da documentação do headscale:

headscale.example.com {
    reverse_proxy 127.0.0.1:8080 {
        header_up True-Client-IP {remote_host}
        header_up X-Real-IP {remote_host}
    }
}
sudo caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl is-active caddy

validate imprime adapted config to JSON quando o arquivo é analisado. Um aviso de que o arquivo não está formatado é apenas cosmético. No seu laptop, curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health também deve imprimir 200. Essa única verificação confirma que o DNS, o firewall, o certificado e o proxy estão funcionando em conjunto.

Este é o detalhe do proxy que pode consumir uma noite inteira. A conexão de controle do Tailscale é uma atualização HTTP, é iniciada com POST em vez de GET, e o valor do cabeçalho Upgrade é tailscale-control-protocol. O Caddy encaminha isso sem configuração adicional. O nginx não faz isso, portanto um front-end nginx precisa do mapa de atualização:

map $http_upgrade $connection_upgrade {
    default keep-alive;
    ''      close;
}

server {
    listen 443 ssl;
    server_name headscale.example.com;
    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_buffering off;
        proxy_pass http://127.0.0.1:8080;
    }
}

Se você omitir essas linhas, as solicitações comuns continuarão funcionando. Por isso, /health retorna 200 e tudo parece correto, enquanto a conexão de controle de longa duração nunca é estabelecida e seus nós são registrados e depois permanecem offline. Se você escolher o nginx, Certbot no Ubuntu 24.04 com nginx aborda a parte do certificado.

Quais portas abrir no UFW

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

A porta 443 transporta toda a comunicação dos clientes. A porta 80 serve apenas para o desafio HTTP do ACME (ambiente de gerenciamento automático de certificados) e para o redirecionamento para HTTPS. O Caddy precisa dela para obter um certificado.

A porta 8080 permanece fechada. listen_addr é 127.0.0.1:8080, portanto o proxy acessa o headscale pela interface de loopback e nenhuma regra de firewall é necessária. Abrir a porta 8080 para a internet fornece aos clientes um canal de controle em texto claro e não traz nenhuma vantagem. Lembre-se de que a maioria dos provedores executa um segundo firewall no painel de controle, separado do UFW. Por isso, uma porta pode estar aberta no servidor e ainda permanecer fechada na borda da rede. Noções básicas sobre o firewall UFW em um VPS apresenta a sintaxe das regras em mais detalhes.

Crie um usuário e uma chave de pré-autenticação

sudo headscale users create alice
sudo headscale users list

O comando headscale é um cliente. Ele se comunica com o daemon em execução pelo socket Unix em /var/run/headscale/headscale.sock, que tem o modo 0770 e pertence ao grupo headscale. Isso tem duas consequências. O comando falha quando o serviço está parado, que é outro motivo para a ordem deste guia ser importante, e requer sudo, a menos que você adicione sua própria conta ao grupo headscale.

users list exibe um ID ao lado de cada nome. Você precisa desse número, porque o comando de chave aceita um ID de usuário numérico, não um nome.

sudo headscale preauthkeys create --user 1 --expiration 24h

A chave é exibida uma única vez. Copie-a agora. Uma chave de pré-autenticação pode ser usada uma única vez e é válida por uma hora, a menos que você especifique outro valor. Por isso, vale a pena definir --expiration 24h enquanto você ainda estiver testando. Adicione --reusable para criar uma chave que inscreva várias máquinas e trate essa chave como uma senha, porque qualquer pessoa que a possua pode ingressar na sua rede.

Conecte seu primeiro cliente com --login-server

Na máquina que você quer adicionar:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server https://headscale.example.com --auth-key 'hskey-auth-PASTE-YOUR-KEY-HERE'
tailscale status
tailscale ip -4

tailscale ip -4 exibe o endereço atribuído pelo headscale, algo como 100.64.0.1. De volta ao servidor, sudo headscale nodes list mostra o nó com seu ID, seu usuário e seu estado online.

O valor de --login-server deve corresponder exatamente a server_url, incluindo o esquema e sem uma barra no final. Eles são comparados como strings, e uma divergência faz o cliente se registrar em um endereço e depois ser instruído a se comunicar com outro.

Uma máquina que tenha sido autenticada anteriormente no serviço hospedado do Tailscale mantém esse login. Execute sudo tailscale logout nela primeiro e depois execute tailscale up com --login-server.

Se você omitir --auth-key, o cliente exibirá uma URL. Abra-a; a página mostrará o identificador dessa tentativa de registro, que você aprova no servidor:

sudo headscale auth register --user alice --auth-id PASTE-THE-ID-FROM-THE-PAGE

Esse formulário é mais conveniente para seu próprio laptop. Chaves de pré-autorização são melhores para qualquer tarefa automatizada, pois não exigem que uma pessoa fique acompanhando o processo.

DERP e o que retransmite o tráfego quando um caminho direto falha

DERP (designated encrypted relay for packets) é o caminho de fallback. Quando dois nós não conseguem abrir uma conexão WireGuard direta, geralmente porque ambos estão atrás de NAT (network address translation) restrito, eles enviam os pacotes por um relay. O relay não possui chaves, portanto não pode ler o tráfego. Ele consegue ver quais nós estão se comunicando e a quantidade de dados transferida.

Entenda claramente o que a configuração padrão faz. O Headscale é distribuído apontando para https://controlplane.tailscale.com/derpmap/default com auto_update_enabled: true e update_frequency: 3h, portanto o seu plano de controle é seu, mas os relays são da Tailscale. Para a maioria das pessoas, essa é uma troca aceitável. Se não for para você, execute o seu próprio relay.

Para executar seu próprio relay, defina enabled: true em derp.server no arquivo config.yaml, reinicie o headscale e abra a porta STUN (session traversal utilities for NAT) com sudo ufw allow 3478/udp. O arquivo de configuração declara o requisito claramente: server_url deve usar https, porque o DERP exige TLS. Esvaziar a lista derp.urls remove os relays da Tailscale do mapa. Se você fizer isso sem um relay incorporado funcionando, qualquer par de nós que não consiga se conectar diretamente não conseguirá se conectar.

Em um cliente, tailscale netcheck exibe a latência para cada região de relay conhecida, e tailscale status marca cada peer como direct com um endereço ou relay com um código de região. Um peer preso em relay indica um problema de NAT, não um problema do headscale.

Por que um node aparece como offline?

O proxy está descartando o upgrade. Esse é o caso mais comum. O sinal é que todo o restante parece normal: /health retorna 200, headscale nodes list mostra o node, e o node nunca fica online. A conexão de controle é um POST que transporta Upgrade: tailscale-control-protocol. Um proxy que não encaminha esse conteúdo interrompe o único canal que informa o estado do node. Compare sua configuração do nginx com o bloco map acima ou mude para o Caddy para eliminar o proxy como causa.

server_url mudou depois que os nodes foram registrados. Os nodes continuam se conectando ao valor recebido durante o registro. Se você alterou esse valor, execute sudo tailscale up --login-server https://headscale.example.com --force-reauth em cada node.

O cliente não está em execução. No node, execute sudo systemctl is-active tailscaled e sudo journalctl -u tailscaled -n 50 --no-pager. Um cliente que não consegue resolver ou alcançar seu domínio registra as tentativas nesses locais.

A chave expirou. Isso é explicado na próxima seção.

Para monitorar o lado do servidor durante o teste, execute sudo journalctl -u headscale -f no VPS e reinicie tailscaled no cliente. Um node que alcança o headscale gera linhas de log imediatamente. Se não houver saída, a solicitação não está chegando. Nesse caso, verifique o DNS, o firewall e o proxy antes de verificar o headscale.

Expiração das chaves e o nó que para de funcionar semanas depois

Existem duas expirações separadas, e confundi-las causa perda de tempo.

As chaves de pré-autorização expiram rapidamente por definição. O padrão é uma hora e um uso. Se tailscale up recusar a chave, gere uma nova no servidor em vez de editar qualquer coisa no cliente.

As chaves dos nós são a parte de longa duração. A seção node de config.yaml define expiry: 0, e 0 significa que não há expiração padrão: um nó registrado permanece válido até que você o expire. Os nós com tags nunca expiram. Defina expiry: 180d se quiser que os registros expirem automaticamente, mas entenda o que isso implica: todos os nós sem tags precisarão de sudo tailscale up --login-server https://headscale.example.com --force-reauth nesse intervalo, e um servidor sem interface gráfica no qual ninguém faça uma nova autenticação será removido da rede por conta própria.

Faça isso manualmente quando alguém perder um laptop. sudo headscale nodes list fornece o ID, depois sudo headscale nodes expire -i 3 desconecta esse nó, e sudo headscale nodes delete -i 3 o remove completamente da rede.

Backups e upgrades

/var/lib/headscale e /etc/headscale form o servidor inteiro. Pare o serviço antes de copiá-los, porque o SQLite pode ter gravações em andamento, e um banco de dados copiado sob carga pode ficar inconsistente.

sudo systemctl stop headscale
sudo tar czf /root/headscale-state.tgz -C /var/lib headscale
sudo tar czf /root/headscale-config.tgz -C /etc headscale
sudo systemctl start headscale
sudo chmod 600 /root/headscale-*.tgz

Mova os dois arquivos para fora do servidor. Eles contêm as chaves privadas e todos os registros, portanto exigem o mesmo cuidado que o próprio servidor. backups restic de um VPS explica como fazer isso de forma programada e criptografada.

As atualizações repetem a instalação: baixe o novo .deb e sudo apt install ./headscale.deb, reinicie e execute novamente as verificações is-active e /health. Desde a versão 0.29, o caminho de atualização é restrito. Não é permitido pular uma versão secundária nem fazer downgrade para uma versão secundária mais antiga. Avance uma versão secundária por vez, faça um backup antes de cada etapa e leia primeiro as notas de versão dessa versão, porque a mesma versão alterou o comportamento da política de ACL e moveu várias chaves de configuração.

FAQ

Por que o headscale falha ao iniciar logo depois que instalo o .deb?

O pacote instala a unidade, mas deixa o serviço parado, e o /etc/headscale/config.yaml padrão é um modelo, não uma configuração funcional. Edite server_url, listen_addr e base_domain primeiro. Depois, execute sudo systemctl enable --now headscale e confirme com sudo systemctl is-active headscale. Se ainda falhar, sudo journalctl -u headscale -n 50 --no-pager identifica o problema. Nesta etapa, quase sempre é um erro de YAML, porque o headscale analisa o arquivo inteiro antes de abrir uma porta.

Ainda preciso instalar o cliente Tailscale normal nas minhas máquinas?

Sim. O Headscale substitui apenas o servidor de controle. Cada nó executa o cliente oficial da Tailscale, e você aponta o cliente para o servidor usando sudo tailscale up --login-server https://headscale.example.com. Essa flag existe no cliente padrão, portanto não é necessário aplicar patches nem recompilar.

Meu tráfego passa pelo servidor headscale?

Normalmente, não. O headscale coordena a rede e distribui chaves e endereços, enquanto o caminho de dados usa WireGuard diretamente entre os nós. O tráfego só faz um desvio quando dois nós não conseguem se conectar diretamente e usam um relay DERP. Com a configuração fornecida, esses relays são os relays públicos da Tailscale. Execute tailscale status em um nó para verificar se determinado peer está direct ou em um relay.

Por que meu nó continua offline depois de se registrar?

Um nó que aparece em headscale nodes list, mas nunca fica online, normalmente perdeu a conexão de controle no proxy reverso. Essa conexão é uma atualização HTTP enviada como POST com o cabeçalho Upgrade: tailscale-control-protocol. O nginx a descarta, a menos que você adicione o bloco map $http_upgrade $connection_upgrade e as linhas proxy_set_header correspondentes. O Caddy encaminha essa conexão sem configuração adicional. Isso permite testar rapidamente se o proxy é a causa do problema.

Preciso de um nome de domínio e TLS para o headscale?

Na prática, sim. Os clientes se conectam à string definida em server_url, os certificados são emitidos para nomes, não para endereços IP simples, e o arquivo de configuração informa que o DERP exige TLS. Um domínio com Caddy leva cerca de cinco minutos para configurar e fornece um endpoint HTTPS que se renova automaticamente. Executar o servidor de controle usando HTTP simples significa que toda comunicação dos clientes com ele atravessa a internet sem criptografia.