VPNSmith
Herramienta gratis - servidor Headscale

Generador de configuración Headscale

Escribe tu dominio, elige Docker o el paquete .deb y cómo se gestiona el HTTPS: el generador escribe config.yaml, el archivo compose o el script de instalación, el archivo del proxy inverso, una política de exit node opcional y los comandos para crear tu primer usuario y conectar un dispositivo. Hecho a partir de los archivos oficiales de Headscale 0.29.5. Nada sale de tu navegador.

Todo se genera en tu navegador. No se envía ni se guarda nada.

Un nombre DNS cuyo registro A (y AAAA) apunta al VPS, por ejemplo headscale.example.com.

Los dispositivos pasan a ser nombre.dominio-base. Debe ser distinto del dominio de arriba; no hace falta que exista públicamente.

Se crea con headscale users create. También es el propietario del tag en la política de exit node.

Lo usan certbot o Headscale para los avisos de caducidad. Caddy funciona sin él.

Método de instalación
HTTPS

Caddy es el camino más corto. Elige nginx si ya corre en el servidor, el TLS integrado si el VPS solo ejecuta Headscale.

DNS de origen para MagicDNS
Opciones
  • Crea el registro DNS del dominio antes de arrancar: la solicitud del certificado falla si el nombre aún no apunta al VPS.

Generado para https://headscale.example.com · Headscale 0.29.5 · 6 archivos

/opt/headscale/config/config.yaml
---
# 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
/opt/headscale/docker-compose.yaml
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:
/opt/headscale/Caddyfile
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}
	}
}
/opt/headscale/config/policy.json
{
  "tagOwners": {
    "tag:exit": [
      "admin@"
    ]
  },
  "autoApprovers": {
    "exitNode": [
      "tag:exit"
    ]
  }
}
setup-headscale.sh
#!/usr/bin/env bash
set -euo pipefail

# 1. Instalar Docker si falta (script oficial)
command -v docker >/dev/null || curl -fsSL https://get.docker.com | sudo sh

# 2. Carpetas para la configuración y los datos
sudo mkdir -p /opt/headscale/config /opt/headscale/lib
cd /opt/headscale

# 3. Guarda antes los archivos generados en estas rutas
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 Headscale
sudo docker compose up -d
sleep 5

# 5. Comprobación: debe mostrar un estado de salud en JSON
curl -fsS http://127.0.0.1:8080/health
first-steps.sh
# Crear el primer usuario
sudo docker exec headscale headscale users create admin

# Anotar el ID numérico del usuario
sudo docker exec headscale headscale users list

# Crear una clave preautenticada (vale 1 hora, un solo uso por defecto)
sudo docker exec headscale headscale preauthkeys create --user <USER_ID>

# En cada dispositivo: iniciar sesión en tu servidor Headscale
sudo tailscale up --login-server https://headscale.example.com --authkey <YOUR_AUTH_KEY>

# En el VPS exit node: iniciar sesión con el tag para la aprobación automática
sudo tailscale up --login-server https://headscale.example.com --authkey <YOUR_AUTH_KEY> \
  --advertise-exit-node --advertise-tags=tag:exit

# Tras editar policy.json: recargar Headscale
sudo docker kill --signal=HUP headscale

# Inicio de sesión por navegador: aprobar el dispositivo con el Auth ID que muestra
sudo docker exec headscale headscale auth register --user admin --auth-id <AUTH_ID>

# Listar los dispositivos conectados
sudo docker exec headscale headscale nodes list

Headscale necesita un servidor siempre encendido

Un VPS pequeño con IPv4 e IPv6 públicas ejecuta estos archivos tal cual, y la misma máquina puede ser el exit node de tu tailnet.

Enlace de afiliado: podemos recibir una comisión sin coste extra para ti. El generador sigue siendo gratis y no depende de ello.

Ver el VPS de Contabo

Qué hacen los archivos generados

Headscale es el sustituto open source del servidor de control de Tailscale: tus dispositivos siguen usando las apps oficiales de Tailscale, pero inician sesión en tu propio servidor con --login-server. Necesita tres cosas: una dirección HTTPS pública en el puerto 443 igual a server_url, un base_domain de MagicDNS distinto de esa dirección y un proxy inverso que deje pasar los upgrades WebSocket si Headscale no gestiona el TLS por sí mismo.

El config.yaml es el archivo de ejemplo completo de la versión 0.29.5 con tus valores: están todas las claves y nada depende de un valor por defecto no documentado. En Docker el contenedor escucha en 0.0.0.0 dentro de su propia red y los puertos solo se publican en 127.0.0.1, salvo 80 y 443 cuando Headscale termina el TLS. Detrás de un proxy inverso en la misma máquina, trusted_proxies apunta a localhost para que las IP reales de los clientes aparezcan en los registros.

Varios comandos cambiaron en las últimas versiones, por eso fallan muchos tutoriales: las claves preautenticadas se crean ahora con el ID numérico del usuario (lo muestra headscale users list), y un dispositivo que inicia sesión por el navegador se aprueba con headscale auth register y su Auth ID. El bloque de primeros pasos usa la sintaxis 0.29.

¿Caddy, nginx o TLS integrado?

Tres formas de servir Headscale por HTTPS, todas documentadas por el proyecto. La correcta depende de lo que ya corre en el servidor.

HTTPS gestionado porCertificadoIdeal cuandoCuidado
Caddy (proxy inverso)Automático, lo renueva CaddyQuieres la instalación más corta u otros sitios en el mismo VPSLos puertos 80 y 443 deben llegar a Caddy; el WebSocket funciona sin ninguna línea extra
nginx (proxy inverso)certbot, lo renueva su temporizadornginx ya sirve otros sitios en la máquinaLas cabeceras Upgrade y Connection son obligatorias, si no los clientes nunca conectan
Let's Encrypt integrado en HeadscaleAutomático, lo renueva HeadscaleEl VPS solo ejecuta HeadscaleHeadscale ocupa los puertos 80 y 443; ningún otro servicio web en esa IP

Preguntas frecuentes

¿Cuál es el config.yaml mínimo de Headscale?
Headscale lee /etc/headscale/config.yaml. Los valores que hay que cambiar son server_url (tu dirección HTTPS pública), listen_addr y dns.base_domain, que debe ser un dominio distinto del de server_url. El punto de partida más seguro es el archivo de ejemplo de tu versión exacta con esos valores editados, que es lo que produce este generador, porque algunas claves cambian de nombre entre versiones.
¿Por qué tailscale up se queda colgado o falla con mi servidor Headscale?
Casi siempre el proxy inverso no deja pasar el upgrade WebSocket (nginx sin las cabeceras Upgrade y Connection), server_url no coincide con la dirección que usan los clientes o el certificado no es de confianza. Prueba curl https://tu-dominio/health desde fuera del servidor y luego mira los registros de Headscale. Usar un puerto distinto de 443 es posible, pero el cliente de Tailscale asume 443 en algunos casos.
¿Cómo creo una clave preautenticada en Headscale 0.29?
Crea un usuario con headscale users create <nombre>, anota su ID numérico con headscale users list y ejecuta headscale preauthkeys create --user <ID>. Por defecto la clave vale una hora y sirve una sola vez. En el dispositivo: tailscale up --login-server https://tu-dominio --authkey <clave>.
¿Necesito el servidor DERP embebido?
No. Por defecto Headscale ofrece a los clientes los relés DERP públicos de Tailscale, que solo se usan cuando no hay conexión directa posible. Activa el DERP embebido si quieres relés bajo tu control: hay que abrir el UDP 3478 e indicar la IPv4 pública (y la IPv6 si la hay) del servidor.
¿Qué tamaño de VPS necesita Headscale?
Muy poco: el servidor de control solo coordina claves y rutas, el tráfico va directo entre dispositivos. Los planes más pequeños bastan para un hogar o un equipo pequeño. Lo importante es una IPv4 pública, idealmente también IPv6, y un proveedor que deje abiertos los puertos 80, 443 y 3478. La misma máquina puede hacer de exit node.