bolt Valebyte VPS desde $4/mes — NVMe, despliegue en 60s.

Obtener VPS arrow_forward
eco Principiante Tutorial/Cómo hacer

Despliegue de Headscale en un VPS: VPN mesh privada, ACL, DNS y conexión de clientes

calendar_month Oct 13, 2026 schedule 18 min de lectura visibility 22 vistas
Развёртывание Headscale на VPS: приватная mesh VPN, ACL, DNS и подключение клиентов
info

¿Necesitas un servidor para esta guía? Ofrecemos servidores dedicados y VPS en más de 50 países con configuración instantánea.

¿Necesitas un VPS para esta guía?

Explore otras opciones de servidores dedicados en

Implementación de Headscale en un VPS: mesh VPN privada, ACL, DNS y conexión de clientes

TL;DR

Headscale es un servidor de control self-hosted para clientes de Tailscale. Permite crear una red mesh VPN propia en un VPS, gestionar nodos, reglas ACL y MagicDNS sin depender del control plane en la nube. En esta guía, Headscale se instala en Ubuntu 24.04 LTS, se protege mediante Caddy y HTTPS, y los clientes se conectan usando Tailscale.

  • Headscale funciona en un VPS pequeño: normalmente bastan 1 vCPU, 1 GB de RAM y 10–20 GB de SSD.
  • Para las conexiones externas solo se abren los puertos TCP 80 y 443; es mejor limitar SSH a la propia dirección IP.
  • Los clientes se integran en una mesh VPN y pueden conectarse directamente cuando es posible.
  • El archivo ACL limita el acceso entre grupos y servidores.
  • MagicDNS permite acceder a los nodos por nombre en lugar de por direcciones IP.
  • Es necesario hacer copias de seguridad de la base de datos de Headscale, la configuración, los datos TLS y la política ACL.

1. TL;DR

Headscale no es un cliente VPN ni una puerta de enlace VPN convencional. Es un servidor de gestión para clientes compatibles con Tailscale: asigna direcciones, distribuye configuraciones, coordina conexiones y aplica políticas de acceso. El tráfico de usuario circula directamente entre los nodos siempre que sea posible, mientras que el VPS se necesita principalmente para la gestión, NAT traversal y el relé DERP cuando no es posible establecer una conexión directa.

A continuación se utiliza el dominio independiente headscale.example.com, Ubuntu 24.04 LTS, Headscale 0.27.x, Caddy 2.x y SQLite. Para un entorno de producción, sustituya el dominio, las direcciones SSH y los secretos por sus propios valores.

2. Contenido

La secuencia está pensada para un VPS nuevo con una dirección IPv4 pública. IPv6 es recomendable, pero no obligatorio. Si el servidor ya se utiliza para otros servicios web, compruebe que los puertos 80 y 443 estén libres o configure un reverse proxy en la infraestructura existente.

3. Qué configuramos y por qué

El papel de Headscale en la infraestructura VPN

Tailscale se construye sobre WireGuard, pero oculta la complejidad de configurar manualmente claves, rutas y NAT. En WireGuard clásico, el administrador crea manualmente la configuración de cada peer. En Headscale, los nodos se registran en un sistema central, reciben direcciones de un rango privado y descubren automáticamente a los demás participantes de la red.

Headscale es una implementación abierta de control server compatible con clientes de Tailscale. El servidor no descifra el tráfico VPN: las claves criptográficas permanecen en los dispositivos finales. Sin embargo, el control server almacena metadatos de la red, la lista de nodos, sus claves públicas y los parámetros de enrutamiento, por lo que también debe protegerse y respaldarse periódicamente.

Qué se obtendrá como resultado

  • Una red privada con direcciones del rango 100.64.0.0/10.
  • Conexión de portátiles, servidores, teléfonos y máquinas virtuales mediante Tailscale.
  • Acceso a SSH, HTTP, bases de datos y servicios internos a través de la VPN.
  • Una política ACL que permite el acceso únicamente a los grupos necesarios.
  • MagicDNS para nombres como git-1.mesh.example.com.
  • HTTPS en el control server y renovación automática del certificado.

Headscale, Tailscale Cloud o WireGuard convencional

Opción Ventajas Limitaciones
WireGuard convencional Mínimo de componentes, control total, alta previsibilidad Las configuraciones de los peers y las rutas deben mantenerse manualmente
Tailscale Cloud Instalación sencilla, panel de administración listo para usar, control plane gestionado Dependencia de un servicio externo y de su política
Headscale en un VPS Gestión independiente del control plane, compatibilidad con clientes de Tailscale Las actualizaciones, copias de seguridad, TLS y monitorización pasan a ser responsabilidad del propietario

La opción self-hosted está justificada cuando se necesita controlar el dominio, los metadatos y las reglas de registro de dispositivos, y cuando el equipo está preparado para mantener un servidor Linux por su cuenta. Para unos pocos dispositivos personales, la opción en la nube suele ser más sencilla. Para decenas o cientos de dispositivos corporativos, Headscale requiere diseñar de antemano el modelo de usuarios, las ACL y la recuperación desde copias de seguridad.

Cómo funciona la conexión

Después del registro, el cliente obtiene claves e información sobre los demás nodos. Los clientes intentan establecer una sesión UDP directa mediante NAT traversal. Si no es posible una conexión directa, se utiliza un relé DERP. El VPS con Headscale no tiene que transportar necesariamente todo el tráfico, por lo que la carga suele ser considerablemente menor que en una puerta de enlace VPN tradicional.

Si se necesita enrutar obligatoriamente el tráfico de Internet a través de un servidor, esa es una tarea independiente: un exit node. Requerirá habilitar IP forwarding, configurar NAT y permitir explícitamente las rutas. En la configuración básica de este artículo, los nodos obtienen acceso entre sí, pero el tráfico de los clientes hacia Internet no se enruta a través del VPS.

4. Qué configuración de VPS se necesita para esta tarea

Requisitos mínimos

Recurso Mínimo Opción práctica
CPU 1 vCPU 2 vCPU
RAM 512 MB, sin mucho tráfico DERP 1–2 GB
Disco 10 GB SSD 20–40 GB SSD
Red IPv4 pública, 100 Mbit/s IPv4 e IPv6 públicas, 1 Gbit/s
SO Ubuntu 24.04 LTS o Debian 12 Ubuntu 24.04 LTS

Para un control server que atienda entre 10 y 50 dispositivos, bastan 1–2 vCPU y 1 GB de RAM. SQLite es adecuado para una instalación pequeña o mediana, siempre que la base de datos se almacene en un SSD local y se copie periódicamente. La variable principal no es el número de nodos registrados, sino el volumen de tráfico a través del relé DERP.

Como opción práctica, puede contratar un VPS con estas características: 2 vCPU, 2 GB de RAM, 30 GB NVMe, IPv4 pública, IPv6 y puerto de 1 Gbit/s. Este margen permite alojar simultáneamente Headscale, Caddy, monitorización y un pequeño servicio DNS privado.

Cuándo se necesita un dedicated

Un servidor dedicado exclusivamente para Headscale suele ser excesivo. Está justificado si el VPS debe funcionar simultáneamente como un relé DERP potente, exit node para un gran número de usuarios, enrutador de varias redes o anfitrión de otros servicios que consuman muchos recursos. Un dedicated también puede ser necesario por requisitos de aislamiento, rendimiento predecible o un gran volumen de tráfico cifrado.

Elección de la ubicación

La ubicación influye en la latencia del control plane y del relé DERP. Con una conexión directa, la geografía de Headscale apenas importa después del intercambio de estado. Al utilizar DERP, la latencia entre los nodos dependerá de la distancia hasta el relé, por lo que para un equipo internacional es razonable ubicar el servidor cerca de la mayoría de los usuarios o desplegar varios relés DERP en distintas regiones.

Compruebe que haya una IPv4 estática y preste atención a las restricciones de UDP y a la política del proveedor respecto a las VPN. Aunque Headscale utilice HTTPS para el control plane, los clientes WireGuard prefieren UDP, por lo que las restricciones de red pueden empeorar las conexiones directas.

5. Preparación del servidor

DNS y parámetros básicos

Antes de la instalación, cree un registro DNS A para headscale.example.com que apunte a la dirección IPv4 del VPS. Si utiliza IPv6, añada un registro AAAA. La comprobación se realiza desde el ordenador local:

# Comprobamos que el dominio apunte a la dirección correcta
dig +short A headscale.example.com
dig +short AAAA headscale.example.com

En los ejemplos siguientes se supone que inicia sesión con un usuario temporal con permisos sudo. No trabaje permanentemente como root.

Creación del administrador y de la clave SSH

En la estación de trabajo, cree una clave Ed25519 si aún no tiene una:

# Creamos un par moderno de claves SSH
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/headscale_ed25519

# Copiamos la clave pública al servidor
ssh-copy-id -i ~/.ssh/headscale_ed25519.pub root@SERVER_IP

Conéctese al servidor y cree un administrador independiente:

# Creamos un usuario para la administración
adduser deploy
usermod -aG sudo deploy

# Creamos el directorio SSH y establecemos los permisos
install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
install -m 600 -o deploy -g deploy /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys

En una nueva ventana de terminal, compruebe el acceso:

# Comprobamos el acceso sin contraseña
ssh -i ~/.ssh/headscale_ed25519 deploy@SERVER_IP

Actualización y paquetes básicos

# Actualizamos los índices e instalamos las correcciones de seguridad
sudo apt update
sudo DEBIAN_FRONTEND=noninteractive apt full-upgrade -y

# Instalamos herramientas de administración y diagnóstico
sudo apt install -y ca-certificates curl wget jq unzip vim-tiny \
  ufw fail2ban sqlite3 dnsutils chrony unattended-upgrades

Habilite la sincronización horaria. Un reloj correcto es necesario para TLS y para el funcionamiento adecuado de las claves de registro temporales:

# Habilitamos el servicio de sincronización horaria
sudo systemctl enable --now chrony

# Comprobamos el estado del reloj
timedatectl status

SSH, firewall y fail2ban

Antes de activar el firewall, asegúrese de que la sesión SSH actual permanezca abierta y de que funcione un nuevo acceso con el usuario deploy. Si SSH solo está disponible desde una red fija, limite el puerto 22 a su dirección.

# Permitimos SSH, HTTP y HTTPS
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Activamos el firewall
sudo ufw --force enable
sudo ufw status verbose

Para restringir SSH adicionalmente, sustituya la regla general por una regla con su dirección pública:

# Ejemplo: permitimos SSH solo desde la red administrativa
sudo ufw delete allow 22/tcp
sudo ufw allow from ADMIN_PUBLIC_IP to any port 22 proto tcp

Desactive la autenticación mediante contraseña solo después de comprobar el acceso mediante clave:

# Creamos una configuración independiente de SSH
sudo tee /etc/ssh/sshd_config.d/ hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
EOF

# Comprobamos la sintaxis y aplicamos la configuración
sudo sshd -t
sudo systemctl reload ssh

En el último comando anterior, en un shell real no debe haber ningún espacio entre el directorio y el nombre del archivo. Utilice la variante correcta:

# Escribimos la configuración SSH en la ruta correcta
sudo tee /etc/ssh/sshd_config.d/hardening.conf >/dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
EOF

sudo sshd -t
sudo systemctl reload ssh
# Iniciamos la protección contra ataques de fuerza bruta a las contraseñas SSH
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd

6. Instalación del software — paso a paso

Paso 1. Determinamos la versión y la arquitectura

Para 2026, para una instalación nueva, utilice la versión estable actual de la rama 0.27.x de Headscale. Antes de realizar el despliegue en producción, compruebe la página de versiones del proyecto y sustituya la variable por la última versión estable. No utilice una URL que cambie automáticamente en infraestructura crítica sin realizar pruebas previas.

# Проверяем архитектуру сервера
uname -m

# Фиксируем версию Headscale для повторяемой установки
export HEADSCALE_VERSION="0.27.1"
export HEADSCALE_ARCH="amd64"

Paso 2. Descargamos el paquete oficial

El paquete de la versión se obtiene del repositorio oficial de Headscale en GitHub. Para ARM64, cambie el valor de la arquitectura a arm64 si el paquete correspondiente está presente en la versión seleccionada.

# Скачиваем Debian-пакет из официального релиза
cd /tmp
curl -fLO "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"

# Устанавливаем Headscale и его systemd unit
sudo apt install -y "./headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"

# Проверяем установленную версию
headscale version

Paso 3. Creamos los directorios y el usuario del sistema

# Создаём каталог данных и конфигурации
sudo install -d -m 750 -o headscale -g headscale /var/lib/headscale
sudo install -d -m 750 -o headscale -g headscale /etc/headscale

# Проверяем, что пакет создал системного пользователя
getent passwd headscale

Paso 4. Creamos la configuración de Headscale

Genere una clave Noise aleatoria. El control server la utiliza para establecer sesiones protegidas con los clientes y no debe incluirse en un repositorio público.

# Генерируем приватный ключ Noise
sudo sh -c 'umask 077; openssl rand -hex 32 > /var/lib/headscale/noise_private.key'

# Назначаем владельца ключа
sudo chown headscale:headscale /var/lib/headscale/noise_private.key

Cree el archivo de configuración. Los valores del dominio y de las subredes deben ser coherentes con las ACL y con su infraestructura existente.

# /etc/headscale/config.yaml
server_url: https://headscale.example.com
listen_addr: 127.0.0.1:8080
metrics_listen_addr: 127.0.0.1:9090

noise:
  private_key_path: /var/lib/headscale/noise_private.key

prefixes:
  v4: 100.64.0.0/10
  v6: fd7a:115c:a1e0::/48

derp:
  server:
    enabled: false
  urls:
    - https://controlplane.tailscale.com/derpmap/default

database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite

dns:
  magic_dns: true
  base_domain: mesh.example.com
  nameservers:
    global:
      - 1.1.1.1
      - 9.9.9.9
  search_domains: []
  extra_records_path: /etc/headscale/dns-records.json

policy:
  mode: file
  path: /etc/headscale/acl.hujson

log:
  level: info
# Защищаем конфигурацию и проверяем владельца
sudo chown root:headscale /etc/headscale/config.yaml
sudo chmod 640 /etc/headscale/config.yaml

Paso 5. Creamos registros DNS para MagicDNS

El archivo de registros adicionales permite dirigir nombres internos a direcciones específicas. En la primera etapa puede dejar una matriz vacía.

[
  {
    "name": "git.mesh.example.com",
    "type": "A",
    "value": "100.64.0.20"
  }
]
# Создаём файл дополнительных DNS-записей
sudo tee /etc/headscale/dns-records.json >/dev/null <<'EOF'
[]
EOF

sudo chown root:headscale /etc/headscale/dns-records.json
sudo chmod 640 /etc/headscale/dns-records.json

Paso 6. Configuramos systemd

Si el paquete no ha instalado una unidad preparada o desea fijar explícitamente los parámetros de inicio, cree su propio archivo de systemd:

# /etc/systemd/system/headscale.service
[Unit]
Description=Headscale coordination server
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=headscale
Group=headscale
ExecStart=/usr/bin/headscale serve
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/headscale
ReadOnlyPaths=/etc/headscale

[Install]
WantedBy=multi-user.target
# Перечитываем unit-файлы и запускаем Headscale
sudo systemctl daemon-reload
sudo systemctl enable --now headscale

# Проверяем статус и последние сообщения журнала
sudo systemctl status headscale --no-pager
sudo journalctl -u headscale -n 50 --no-pager

Paso 7. Instalamos Caddy

Caddy aceptará solicitudes HTTPS, obtendrá un certificado de Let’s Encrypt y enviará las solicitudes a Headscale en localhost mediante proxy. Utilice el repositorio oficial de Caddy.

# Устанавливаем ключ и репозиторий Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | \
  sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | \
  sudo tee /etc/apt/sources.list.d/caddy-stable.list

# Устанавливаем Caddy 2.x из официального репозитория
sudo apt update
sudo apt install -y caddy

Paso 8. Conectamos el reverse proxy HTTPS

# /etc/caddy/Caddyfile
headscale.example.com {
    reverse_proxy 127.0.0.1:8080

    header {
        Strict-Transport-Security "max-age=31536000"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "no-referrer"
    }

    log {
        output file /var/log/caddy/headscale-access.log
        format json
    }
}
# Проверяем синтаксис Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile

# Запускаем Caddy и применяем конфигурацию
sudo systemctl enable --now caddy
sudo systemctl reload caddy

# Проверяем HTTPS-ответ
curl -I https://headscale.example.com/

La respuesta a la URL raíz puede no ser una página HTML. Lo importante es que la conexión TLS sea correcta y que no haya errores del reverse proxy. Si no se emite el certificado, compruebe el DNS, la disponibilidad del puerto TCP 80 y que ningún otro proceso esté utilizando el puerto.

7. Configuración

Creación del primer usuario

En Headscale, el usuario es el propietario lógico de los nodos. No lo confunda con un usuario de Linux. Cree espacios separados para dispositivos personales, servidores o equipos diferentes.

# Создаём пользователя для администрирования устройств
sudo headscale users create admin

# Показываем список пользователей и запоминаем имя
sudo headscale users list

Política ACL

Es preferible almacenar la ACL en Git, pero no incluya secretos en el repositorio. A continuación se muestra un ejemplo de política para dos grupos: los administradores pueden conectarse a los servidores, mientras que los desarrolladores solo pueden acceder al servidor Git y al puerto SSH de las aplicaciones. La sintaxis utiliza HUJSON: se permiten comentarios y las comillas alrededor de las claves pueden conservarse para mayor claridad.

{
  "groups": {
    "group:admins": [
      "admin@",
    ],
    "group:developers": [
      "dev@",
    ],
  },

  "hosts": {
    "git": "100.64.0.20",
    "app": "100.64.0.21",
    "db": "100.64.0.22",
  },

  "acls": [
    {
      "action": "accept",
      "src": ["group:admins"],
      "dst": [":"],
    },
    {
      "action": "accept",
      "src": ["group:developers"],
      "dst": ["git:22", "git:443", "app:22"],
    },
  ],

  "ssh": [
    {
      "action": "check",
      "src": ["group:admins"],
      "dst": ["autogroup:tagged"],
      "users": ["autogroup:nonroot", "root"],
    },
    {
      "action": "check",
      "src": ["group:developers"],
      "dst": ["git"],
      "users": ["autogroup:nonroot"],
    },
  ],
}

Las direcciones del campo hosts deben corresponder a las direcciones de los nodos en Headscale. En un entorno real, es útil utilizar etiquetas para los servidores y grupos separados para las personas. No añada la regla : sin necesidad: convierte la ACL en un permiso para toda la red mesh.

# Проверяем, что файл существует и читается
sudo stat /etc/headscale/acl.hujson

# Перезапускаем Headscale после изменения политики
sudo systemctl restart headscale

# Проверяем журнал на ошибки разбора конфигурации
sudo journalctl -u headscale -n 80 --no-pager

Registro del cliente mediante una pre-auth key

Para los servidores, resulta más práctico utilizar una pre-auth key de un solo uso o de corta duración. No la transmita por chats públicos ni la guarde en el historial del shell si no es necesario.

# Создаём ключ регистрации на 24 часа для пользователя admin
sudo headscale preauthkeys create \
  --user admin \
  --expiration 24h

Copie la clave mostrada y ejecútela en el servidor Linux cliente:

# Устанавливаем Tailscale-клиент на Debian/Ubuntu
curl -fsSL https://tailscale.com/install.sh | sh

# Подключаем узел к собственному Headscale
sudo tailscale up \
  --login-server=https://headscale.example.com \
  --authkey=PASTE_PREAUTH_KEY \
  --accept-dns=true

# Проверяем локальное состояние клиента
sudo tailscale status

Para realizar un registro interactivo sin una pre-auth key, ejecute tailscale up --login-server=https://headscale.example.com. El cliente mostrará una URL. Ábrala en el navegador y registre después el nodo que aparecerá en el servidor:

# Показываем ожидающие регистрации узлы
sudo headscale nodes list

# Привязываем узел к пользователю по его идентификатору
sudo headscale nodes register --user admin --key nodekey:PASTE_NODE_KEY

# Снова выводим список зарегистрированных узлов
sudo headscale nodes list

Nombres de los nodos y comprobación de la conexión

# Переименовываем узел для понятного отображения
sudo headscale nodes rename --identifier NODE_ID --new-name git-1

# Проверяем узлы на сервере Headscale
sudo headscale nodes list

# На клиенте проверяем адрес и состояние mesh VPN
tailscale ip
tailscale status
tailscale netcheck

Compruebe la conexión desde otro nodo:

# Проверяем доступ по VPN-адресу
ping -c 3 100.64.0.20

# Проверяем SSH через mesh-сеть
ssh [email protected]

# Проверяем MagicDNS-имя
getent hosts git-1.mesh.example.com
ping -c 3 git-1.mesh.example.com

DNS y MagicDNS

MagicDNS añade los nombres de los dispositivos registrados al dominio indicado en base_domain. Los clientes Tailscale deben aceptar la configuración DNS, por lo que el parámetro --accept-dns=true es importante. Si en la estación de trabajo se utiliza un gestor DNS local, como systemd-resolved, NetworkManager o un agente corporativo, compruebe quién administra realmente el DNS.

No utilice el dominio de la red mesh como una zona DNS pública con registros en conflicto. Es práctico reservar un subdominio independiente, por ejemplo mesh.example.com, y no publicarlo en la zona pública, o dejar allí únicamente los registros necesarios.

Rutas a la red privada

Para acceder a la LAN mediante un nodo que actúe como enrutador de subred, es necesario activar el forwarding en dicho nodo, anunciar la ruta y autorizarla en el servidor Headscale. Es un área de riesgo independiente: un error en las rutas puede exponer redes internas a usuarios no autorizados.

# На Linux-сабнет-роутере включаем IPv4 forwarding
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-tailscale-forwarding.conf
sudo sysctl --system

# Объявляем внутреннюю подсеть через Tailscale
sudo tailscale up \
  --advertise-routes=192.168.10.0/24 \
  --accept-dns=true

Después, la ruta debe aprobarse mediante Headscale y restringirse con ACL. No anuncie la ruta 0.0.0.0/0 si no pretende crear un exit node.

8. Copias de seguridad y mantenimiento

Qué se debe respaldar

  • /var/lib/headscale/db.sqlite — nodos registrados, usuarios y estado del servidor.
  • /etc/headscale/config.yaml — configuración principal.
  • /etc/headscale/acl.hujson — política de acceso.
  • /etc/headscale/dns-records.json — registros DNS adicionales.
  • /var/lib/headscale/noise_private.key — clave privada crítica de Headscale.
  • /etc/caddy/Caddyfile y el directorio de certificados de Caddy.

La exposición de la copia de seguridad de la clave Noise y de la base de datos puede proporcionar a un atacante información valiosa sobre el control plane. Guarde los archivos en un almacenamiento independiente con cifrado, limite el acceso y no coloque la única copia en el mismo VPS.

Copia de seguridad simple con restic

A continuación se muestra un ejemplo con almacenamiento compatible con S3. Los valores de conexión se pasan mediante un archivo de entorno independiente con permisos 600. Para producción, utilice un bucket con versioning y una clave de acceso independiente que solo tenga permiso para el bucket necesario.

# Instalamos restic desde el repositorio de Ubuntu
sudo apt install -y restic

# Creamos un archivo con los parámetros del repositorio
sudo install -m 600 /dev/null /root/.restic-headscale.env
sudo tee /root/.restic-headscale.env >/dev/null <<'EOF'
export RESTIC_REPOSITORY="s3:https://s3.example.net/headscale-backups"
export AWS_ACCESS_KEY_ID="CHANGE_ME"
export AWS_SECRET_ACCESS_KEY="CHANGE_ME"
export RESTIC_PASSWORD="CHANGE_ME_LONG_RANDOM_PASSWORD"
EOF

# Inicializamos el repositorio una vez
sudo bash -c 'source /root/.restic-headscale.env && restic init'

El script primero crea un volcado de SQLite y luego archiva los archivos de configuración y los certificados:

# /usr/local/sbin/backup-headscale
#!/usr/bin/env bash
set -euo pipefail

source /root/.restic-headscale.env

backup_dir="$(mktemp -d)"
trap 'rm -rf "$backup_dir"' EXIT

# Creamos una copia coherente de SQLite
sqlite3 /var/lib/headscale/db.sqlite ".backup '$backup_dir/headscale.sqlite'"

# Copiamos la configuración, las claves y los ajustes TLS
install -d "$backup_dir/etc-headscale" "$backup_dir/caddy"
cp -a /etc/headscale/. "$backup_dir/etc-headscale/"
cp -a /etc/caddy/Caddyfile "$backup_dir/caddy/"

# Cargamos el archivo en el almacenamiento remoto
restic backup "$backup_dir" --tag headscale

# Eliminamos puntos de restauración antiguos
restic forget --tag headscale --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
# Establecemos permisos para el script
sudo chmod 700 /usr/local/sbin/backup-headscale

# Ejecutamos una copia de seguridad de prueba
sudo /usr/local/sbin/backup-headscale

# Añadimos la ejecución diaria a cron
echo '15 03   * root /usr/local/sbin/backup-headscale >> /var/log/headscale-backup.log 2>&1' | \
  sudo tee /etc/cron.d/headscale-backup

Compruebe periódicamente la restauración:

# Vemos los puntos de restauración disponibles
sudo bash -c 'source /root/.restic-headscale.env && restic snapshots'

# Comprobamos la integridad del repositorio
sudo bash -c 'source /root/.restic-headscale.env && restic check'

Actualizaciones

Headscale es un servicio stateful, por lo que debe actualizarse durante una maintenance window. Antes de actualizar, guarde una copia de seguridad, lea las release notes y compruebe la compatibilidad del formato de configuración. No actualice simultáneamente Headscale, el SO, Caddy y las aplicaciones cliente sin poder identificar el origen de un problema.

# Detenemos el servicio antes de reemplazar el paquete binario
sudo systemctl stop headscale

# Instalamos el nuevo paquete, descargado y verificado previamente
sudo apt install -y /tmp/headscale_NEW_VERSION_linux_amd64.deb

# Iniciamos el servicio y comprobamos el registro
sudo systemctl start headscale
sudo systemctl status headscale --no-pager
sudo journalctl -u headscale -n 100 --no-pager

Para un único VPS, un rolling update no es posible: durante el reinicio del control plane, el registro y la actualización del estado de los clientes pueden no estar disponibles, aunque las conexiones VPN ya establecidas a veces siguen funcionando. Si necesita alta disponibilidad, diseñe una arquitectura independiente con una base de datos externa, varios control server y soporte probado para dicho esquema en la versión específica de Headscale.

Observabilidad

Como mínimo, supervise el estado de systemd, la vigencia del certificado TLS, el uso del disco, la disponibilidad de HTTPS y la antigüedad de la última copia de seguridad.

# Comprobación rápida del estado de los servicios y del disco
systemctl is-active headscale caddy
df -h /
curl -fsS https://headscale.example.com/ >/dev/null
sudo journalctl -u headscale --since "1 hour ago" --no-pager

9. Troubleshooting y FAQ

Headscale no se inicia después de cambiar config.yaml

Primero revise el registro: sudo journalctl -u headscale -n 100 --no-pager. Las causas frecuentes son un error de YAML, una ruta incorrecta a la base de datos o a la clave Noise, falta de permisos para el usuario headscale y parámetros de otra versión. Asegúrese de que el servicio pueda leer el archivo y de que el directorio /var/lib/headscale tenga permiso de escritura. Después de corregirlo, ejecute sudo systemctl restart headscale y vuelva a comprobar el estado.

El cliente informa de un problema con el login server o TLS

Compruebe el registro DNS con el comando dig, la disponibilidad de https://headscale.example.com mediante curl -Iv y los registros de Caddy. Los puertos TCP 80 y 443 deben estar permitidos en UFW y en el firewall externo del proveedor. Si no se emite el certificado, asegúrese de que el dominio apunte exactamente a este VPS y de que otro servicio web no esté utilizando el puerto 80. El comando del cliente debe utilizar la URL completa con HTTPS.

El nodo aparece, pero no es visible en la red mesh

En el servidor, ejecute headscale nodes list, y en el cliente, tailscale status y tailscale netcheck. Compruebe que el nodo esté realmente registrado para el usuario correcto y que su firewall local permita la interfaz Tailscale. Un error de ACL también puede parecer una falta de acceso, aunque el registro se haya completado correctamente. Para el diagnóstico, compruebe temporalmente el permiso para una dirección y un puerto específicos sin abrir toda la red.

MagicDNS no resuelve nombres

Asegúrese de que dns.magic_dns: true esté habilitado en la configuración, que se haya definido base_domain y que el cliente se ejecute con --accept-dns=true. Los comandos tailscale ip y tailscale status ayudarán a confirmar el estado del cliente. En Linux, compruebe resolvectl status; una VPN de terceros, NetworkManager o un agente DNS corporativo puede sobrescribir la configuración. Si solo necesita acceso por IP, puede deshabilitar temporalmente DNS para separar el problema de red del problema de resolución de nombres.

La conexión funciona mediante DERP, pero es lenta

Ejecute tailscale netcheck en ambos nodos y compruebe si UDP está disponible y qué región DERP se ha seleccionado. El síntoma suele significar que NAT o el firewall bloquea una sesión UDP directa. Permita UDP saliente y compruebe si el proveedor utiliza NAT simétrico. Para usuarios remotos, puede implementar relés DERP adicionales más cerca de sus regiones, pero primero asegúrese de que el problema realmente afecte al rendimiento y no esté relacionado con la sobrecarga del servidor final.

¿Qué configuración de VPS es mínimamente adecuada?

Para Headscale en sí, bastan 1 vCPU, 512 MB de RAM, 10 GB de SSD e IPv4 público. Es más práctico elegir 2 vCPU, 1–2 GB de RAM y 20–40 GB de SSD: esto deja margen para Caddy, registros, monitorización y algo de tráfico DERP. Más importante que el rendimiento es disponer de TCP 80/443 abiertos, una red estable y copias de seguridad. Si un gran volumen de tráfico de relé o de exit node pasa por el servidor, priorice sobre todo el ancho de banda y la CPU.

¿Qué elegir: VPS o dedicated para esta tarea?

Para un control server, casi siempre basta con un VPS. Se necesita un dedicated no por Headscale en sí, sino por la carga asociada: un relé DERP grande, un exit node, el enrutamiento de varias redes de oficina o requisitos estrictos de aislamiento. Comience con un VPS y mida CPU, RAM, tráfico de red y latencia. Tiene sentido migrar a dedicated cuando las limitaciones de virtualización o del enlace de red estén confirmadas por métricas y no solo supuestas de antemano.

¿Se puede utilizar SQLite en producción?

Para un solo Headscale con un número reducido de nodos, SQLite es adecuado si la base de datos se encuentra en un SSD local y se realizan copias de seguridad coherentes periódicamente. No coloque el archivo de base de datos en un sistema de archivos de red ni lo copie con un simple cp mientras haya escritura activa. Para una instalación grande, estudie con antelación las opciones de base de datos externa compatibles con las versiones de Headscale y pruebe las migraciones en una copia.

¿Es seguro exponer Headscale a Internet?

Solo debe ser público el endpoint HTTPS del control server; la propia base de datos y el SSH administrativo no deben estar accesibles para todos. Utilice versiones actuales, HTTPS, SSH restringido, fail2ban, firewall y pre-auth keys de corta duración. ACL no sustituye las actualizaciones ni la protección de los dispositivos finales. Recuerde que Headscale gestiona el registro de nodos: la exposición del VPS o de sus claves privadas requiere un análisis inmediato, la rotación de secretos y, posiblemente, la reemisión de la infraestructura de confianza.

10. Conclusiones y próximos pasos

Como resultado, Headscale funciona en un VPS detrás de Caddy y HTTPS, los clientes Tailscale están unidos en una VPN mesh privada y el acceso entre nodos está controlado por ACL y MagicDNS. La configuración no requiere pasar todo el tráfico de usuarios por el VPS, por lo que normalmente basta con un servidor pequeño.

A continuación, conviene comprobar la restauración desde la copia de seguridad, añadir monitorización de la vigencia del certificado y del disco, y después formalizar grupos, etiquetas y reglas ACL. A medida que aumente la carga, puede desplegar relés DERP en regiones adicionales, añadir rutas de subred o diseñar por separado un exit node con acceso restringido.

¿Te fue útil esta guía?

Tus comentarios nos ayudan a mejorar nuestras guías.

Compartir esta publicación:

Envía esta guía a alguien a quien pueda resultarle útil.

Telegram VKVK WhatsApp Facebook LinkedIn XX

despliegue de headscale en VPS: VPN mesh privada, ACL, DNS y conexión de clientes
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.