📁 文件部署位置

将生成的配置文件复制到服务器对应位置:

# 创建必要目录

mkdir -p /opt/headscale/{config,headplane,data/storage,headplane/data/storage}

# 复制配置文件

cp headplane-config.yaml /opt/headscale/plane/config.yaml

cp headscale-config.yaml /opt/headscale/config.yaml

cp dns_records.json /opt/headscale/dns_records.json

cp compose.yaml /opt/headscale/

⚙️ 配置修改清单

1. Headscale 配置 (/opt/headscale/config.yaml)

# Headscale Configuration File Reference

# Save this as: /opt/headscale/config.yaml

# Full example: https://github.com/juanfont/headscale/blob/main/config-example.yaml

# Server URL that clients will connect to

# MUST be accessible from all clients and use HTTPS in production

server_url: https://headscale.example.com

# Address to listen on

listen_addr: 0.0.0.0:8080

# Metrics endpoint (Prometheus)

metrics_listen_addr: 0.0.0.0:9090

# gRPC endpoint for CLI (local only recommended)

grpc_listen_addr: 127.0.0.1:50443

grpc_allow_insecure: false

# Noise protocol private key

noise:

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

# IP prefix allocation

prefixes:

  v4: 100.64.0.0/10

  v6: fd7a:115c:a1e0::/48

  allocation: sequential

# DERP (relay) configuration

derp:

  server:

    # Enable embedded DERP server

    enabled: true

    

    # Region configuration

    region_id: 999

    region_code: "headscale"

    region_name: "Headscale Embedded DERP"

    

    # Only allow clients from this server

    verify_clients: true

    

    # STUN listener for NAT traversal (REQUIRED for DERP)

    stun_listen_addr: "0.0.0.0:3478"

    

    # Private key for DERP encryption

    private_key_path: /var/lib/headscale/derp_server_private.key

    

    # Auto-add to DERP map

    automatically_add_embedded_derp_region: true

    

    # Server public IPs (IMPORTANT: set your actual IPs)

    ipv4: 198.51.100.1  # CHANGE THIS to your server's IPv4

    ipv6: 2001:db8::1   # CHANGE THIS or remove if no IPv6

  

  # External DERP servers (Tailscale's official servers)

  urls:

    # Uncomment to use Tailscale's DERP servers as backup

    # - https://controlplane.tailscale.com/derpmap/default

  

  # Custom DERP map files

  paths: []

  

  # Auto-update DERP map

  auto_update_enabled: true

  update_frequency: 3h

# Disable update checks

disable_check_updates: false

# Ephemeral node timeout

ephemeral_node_inactivity_timeout: 30m

# Database configuration

database:

  type: sqlite

  debug: false

  

  gorm:

    prepare_stmt: true

    parameterized_queries: true

    skip_err_record_not_found: true

    slow_threshold: 1000

  

  sqlite:

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

    write_ahead_log: true

    wal_autocheckpoint: 1000

# TLS/ACME configuration (if not using reverse proxy)

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"

# Custom TLS certificates

tls_cert_path: ""

tls_key_path: ""

# Logging

log:

  level: info

  format: text

# ACL Policy

policy:

  # Mode: "file" or "database" (database recommended for Headplane)

  mode: database

  

  # If using file mode, specify path

  # path: ""

# DNS Configuration

dns:

  # MagicDNS base domain (must be different from server_url domain)

  base_domain: example.com

  

  # Override local DNS settings

  override_local_dns: true

  

  # Nameservers

  nameservers:

    global:

      - 1.1.1.1

      - 1.0.0.1

      - 2606:4700:4700::1111

      - 2606:4700:4700::1001

  

  # Split DNS (optional)

  split: {}

  

  # Search domains

  search_domains: []

  

  # Extra DNS records (inline)

  extra_records: []

  

  # Extra DNS records from file (RECOMMENDED for Headplane)

  extra_records_path: /var/lib/headscale/dns_records.json

# Unix socket for CLI

unix_socket: /var/run/headscale/headscale.sock

unix_socket_permission: "0770"

# OpenID Connect (OIDC) - OPTIONAL

# oidc:

#   only_start_if_oidc_is_available: true

#   issuer: "https://your-oidc.issuer.com/path"

#   client_id: "your-oidc-client-id"

#   client_secret: "your-oidc-client-secret"

#   # Or use file:

#   # client_secret_path: "${CREDENTIALS_DIRECTORY}/oidc_client_secret"

#   expiry: 180d

#   use_expiry_from_token: false

#   scope: ["openid", "profile", "email"]

#   extra_params: {}

#   allowed_domains: []

#   allowed_groups: []

#   allowed_users: []

#   strip_email_domain: true

#   map_legacy_users: true

# Logtail (Tailscale telemetry) - disabled by default

logtail:

  enabled: false

# Randomize WireGuard client port (firewall workaround)

randomize_client_port: false

必须修改的项:

  • server_url: 改为你的域名,如 https://hc.example.com

  • derp.server.ipv4: 改为你服务器的公网 IPv4 地址

  • derp.server.ipv6: 如无 IPv6 则删除此行

  • dns.base_domain: 改为你的内网域名,如 tailnet.local

建议修改的项:

  • derp.server.region_code: 自定义区域代码

  • derp.server.region_name: 自定义区域名称

2. Headplane 配置 (/opt/headscale/plane/config.yaml)

# Headplane Configuration File

# Save this as: /opt/headscale/plane/config.yaml

# Server configuration

server:

  host: "0.0.0.0"

  port: 3000

  # Cookie secret: MUST be exactly 32 characters

  # Generate with: openssl rand -base64 24

  cookie_secret: "ePBznSrMg27mFSjyMsbEc8p4TB4Kch6y"

  

  # Set to true if using HTTPS (via reverse proxy), false for HTTP

  cookie_secure: false

  

  # Session cookie max age (1 day = 86400 seconds)

  cookie_max_age: 86400

  

  # Optional: restrict cookie to specific domain

  # cookie_domain: "headscale.example.com"

  

  # Data persistence path (must be mounted in Docker)

  data_path: "/var/lib/headplane"

# Headscale integration settings

headscale:

  # Headscale API URL (use container name for Docker network)

  url: "http://headscale:8080"

  

  # Optional: public URL if different from API URL

  # public_url: "https://headscale.example.com"

  

  # Path to Headscale config file (must be shared volume)

  config_path: "/etc/headscale/config.yaml"

  

  # Strict config validation (recommended)

  config_strict: true

  

  # Path to DNS extra records file (optional but recommended)

  dns_records_path: "/etc/headscale/dns_records.json"

  

  # Optional: path to TLS cert if Headscale uses custom TLS

  # tls_cert_path: "/var/lib/headplane/tls.crt"

# Integration with container orchestration

integration:

  # Headplane Agent (for SSH and node info)

  agent:

    # Enable the agent for advanced features

    enabled: false

    

    # Pre-auth key for agent to join Tailnet

    # Generate with: headscale preauthkeys create --user <user> --expiration 90d

    # pre_authkey: "<your-preauth-key>"

    

    # Optional: custom hostname for agent

    # host_name: "headplane-agent"

    

    # Cache settings

    # cache_ttl: 60

    # cache_path: "/var/lib/headplane/agent_cache.json"

    

    # Working directory for agent data

    # work_dir: "/var/lib/headplane/agent"

  # Docker integration (for Headscale restarts on config changes)

  docker:

    enabled: true

    

    # Label to identify Headscale container

    container_label: "me.tale.headplane.target=headscale"

    

    # Optional: fallback to container name if label not found

    # container_name: "headscale"

    

    # Docker socket path

    socket: "unix:///var/run/docker.sock"

  # Kubernetes integration (alternative to Docker)

  kubernetes:

    enabled: false

    # validate_manifest: true

    # pod_name: "headscale"

  # Native process integration (non-containerized deployments)

  proc:

    enabled: false

# OIDC (Single Sign-On) configuration - OPTIONAL

# Uncomment and configure if you want SSO login

# oidc:

#   issuer: "https://accounts.google.com"

#   

#   # Client ID and secret from your OIDC provider

#   client_id: "your-client-id.apps.googleusercontent.com"

#   client_secret: "<your-client-secret>"

#   

#   # Or read secret from file

#   # client_secret_path: "${CREDENTIALS_DIRECTORY}/oidc_client_secret"

#   

#   # OAuth scopes

#   # scope: "openid email profile"

#   

#   # Headscale API key for OIDC users

#   # Generate with: headscale apikeys create --expiration 999d

#   # headscale_api_key: "<your-headscale-api-key>"

#   

#   # Redirect URI (must match your OIDC provider config)

#   # redirect_uri: "https://headscale.example.com/admin/oidc/callback"

#   

#   # Disable regular API key login when using OIDC

#   # disable_api_key_login: false

#   

#   # Token endpoint auth method

#   # token_endpoint_auth_method: "client_secret_post"

#   

#   # Profile picture source: "oidc" or "gravatar"

#   # profile_picture_source: "gravatar"

#   

#   # Manual endpoint configuration (if provider doesn't support discovery)

#   # authorization_endpoint: ""

#   # token_endpoint: ""

#   # userinfo_endpoint: ""

#   

#   # Extra query parameters for authorization

#   # extra_params:

#   #   prompt: "select_account"

必须修改的项:

  • erver.cookie_secret: 生成32字符随机字符串

openssl rand -base64 24

条件修改的项:

  • server.cookie_secure:

    • 使用 HTTPS(反向代理)→ true

    • 直接使用 HTTP → false

  • headscale.url: 如果 Headscale 使用自定义端口,需修改

3. DNS 记录文件 (/opt/headscale/dns_records.json)

已创建空文件,Headplane 会自动管理。如需手动添加记录:

[

  {

    "name": "service.tailnet.local",

    "type": "A",

    "value": "100.64.0.10"

  }

]

🚀 部署步骤

1. 启动服务

cd /opt/headscale

docker compose up -d

2. 检查容器状态

docker compose ps

docker compose logs -f

3. 创建 Headscale API Key

# 创建永久 API Key(用于 Headplane 登录)

docker exec headscale headscale apikeys create --expiration 90d

# 查看所有 API Keys

docker exec headscale headscale apikeys list

将生成的 API Key 保存,登录 Headplane 时需要。

4. 访问 Headplane

浏览器访问:http://你的服务器IP:3000/admin

使用上一步创建的 API Key 登录。

🔧 进阶配置

启用 Headplane Agent(获取节点详细信息)

1. 创建专用预授权密钥:

docker exec headscale headscale users create headplane-agent

docker exec headscale headscale preauthkeys create --user headplane-agent --reusable --expiration 365d

2. 修改 /opt/headscale/plane/config.yaml

integration:

  agent:

    enabled: true

    pre_authkey: "<上面生成的密钥>"

3. 重启 Headplane:

docker compose restart headplane

配置 OIDC 单点登录(可选)

参考两个配置文件中的 OIDC 注释部分,配置步骤:

1. 在 OIDC 提供商(Google/GitHub/Keycloak等)创建应用

2. 配置回调 URL:https://你的域名/admin/oidc/callback

3. 在 Headscale 和 Headplane 配置文件中填写 OIDC 信息

4. 重要:确保两者使用相同的 client_id

### 反向代理配置(Caddy 示例)

headscale.example.com {

    # Headplane Admin UI

    handle /admin* {

        reverse_proxy localhost:3000

    }

    

    # Headscale API

    handle {

        reverse_proxy localhost:8080

    }

}

🔍 故障排查

查看日志

# 查看所有日志

docker compose logs -f

# 只看 Headplane 日志

docker compose logs -f headplane

# 只看 Headscale 日志

docker compose logs -f headscale

常见问题

1. Headplane 无法连接 Headscale

  • 检查 headscale.url 配置是否正确

  • 确认 Headscale 容器已启动:docker compose ps

2. DNS 设置无法保存

  • 确认 dns_records.json 文件权限正确

  • 检查 Docker volume 挂载路径一致

3. Cookie 相关错误

  • 确认 cookie_secret 为32字符

  • HTTP 环境下 cookie_secure 必须为 false

4. Headscale 重启失败

  • 检查 Docker socket 挂载:/var/run/docker.sock:/var/run/docker.sock:ro

  • 确认容器 label 正确:me.tale.headplane.target: headscale

📊 监控

Prometheus 指标端点:http://你的服务器IP:9090/metrics

可配置 Prometheus + Grafana 进行监控。

🔄 更新

cd /opt/headscale

docker compose pull

docker compose up -d

📚 参考资料