Saltar a contenido

⚠️ Headplane dado de baja (jul 2026). Ver nota en Docker/Headscale.md.

Headscale

Servidor de coordinación self-hosted para Tailscale/WireGuard mesh VPN. Reemplazo de Tailscale SaaS — los clientes Tailscale siguen funcionando, solo cambian el --login-server a este servidor.

📁 Rutas y configs

Qué Dónde
Compose /opt/docker/headscale/compose.yaml
Config /opt/docker/headscale/config/config.yaml
Data (DB, claves) /opt/docker/headscale/data/
Compose backup /opt/docker/headscale/compose.yaml.bak

Contenedores: headscale (API, puerto 8528) + headplane (Web UI, puerto 8530) Red: headscale_net (bridge) Imagen: headscale/headscale:0.29.1 (distroless) + ghcr.io/tale/headplane:latest Subdominio: https://headscale.matias7235.com.ar (API) + https://headplane.matias7235.com.ar (UI)

Compose

services:
  headscale:
    image: headscale/headscale:0.29.1
    container_name: headscale
    restart: unless-stopped
    command: ["serve"]
    ports:
      - "127.0.0.1:8528:8080"
    volumes:
      - ./config:/etc/headscale:ro
      - ./data:/var/lib/headscale
    environment:
      - TZ=America/Argentina/Buenos_Aires
    networks:
      - headscale_net
    healthcheck:
      test: ["CMD", "headscale", "health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 15s
    logging:
      driver: json-file
      options:
        max-size: 10m
        max-file: 3

  headplane:
    image: ghcr.io/tale/headplane:latest
    container_name: headplane
    restart: unless-stopped
    ports:
      - "127.0.0.1:8530:3000"
    volumes:
      - ./config:/etc/headplane:ro
      - ./data:/var/lib/headplane
      - /opt/docker/headscale/config/config.yaml:/etc/headscale/config.yaml:rw
    environment:
      - TZ=America/Argentina/Buenos_Aires
      - HEADPLANE_CONFIG_PATH=/etc/headplane/config.yaml
    networks:
      - headscale_net
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    logging:
      driver: json-file
      options:
        max-size: 10m
        max-file: 3

networks:
  headscale_net:
    name: headscale_net
    driver: bridge

Notas sobre permisos y seguridad

  • NO usar read_only: true en el contenedor headscale: el binario genera claves Noise en /var/lib/headscale/ en el primer arranque. Con read_only: true falla con permission denied al escribir noise_private.key.
  • cap_drop: [ALL] no se aplica a headscale: el binario distroless corre como root y necesita acceso completo al bind mount.
  • headplane SÍ admite cap_drop: [ALL] + security_opt: no-new-privileges: es una app Node.js que escribe a su propia DB en /var/lib/headplane.
  • Permisos del bind mount de data: la primera vez que se inicia con éxito, los archivos en /opt/docker/headscale/data/ quedan con owner=root (UID 0). El subdirectorio /opt/docker/headscale/data/ queda con owner=65532:65532 (UID del usuario nonroot del binario distroless) si se hizo chown 65532:65532 antes del primer arranque. Una vez headscale corre, los archivos de estado (DB, claves) son de root. No modificar.
  • Headplane requiere integration.agent.pre_authkey: aunque el agent esté deshabilitado, el campo debe estar presente (puede ser string vacío).