{"captainVersion":2,"dockerCompose":{"services":{"$$cap_appname":{"volumes":["$$cap_appname-data:/var/lib/headscale"],"restart":"always","containerHttpPort":"8080","dockerfileLines":["FROM debian:bookworm-slim AS headscale-config","RUN mkdir -p /etc/headscale /var/lib/headscale /var/run/headscale","RUN printf '%s\\n' 'server_url: https://$$cap_public_domain' 'listen_addr: 0.0.0.0:8080' 'metrics_listen_addr: 127.0.0.1:9090' 'grpc_listen_addr: 127.0.0.1:50443' 'grpc_allow_insecure: false' '' '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' '  urls:' '    - https://controlplane.tailscale.com/derpmap/default' '  paths: []' '  auto_update_enabled: true' '  update_frequency: 3h' '' 'disable_check_updates: true' 'ephemeral_node_inactivity_timeout: 30m' '' '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' '' '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: \"\"' '' 'dns:' '  magic_dns: true' '  base_domain: $$cap_tailnet_domain' '  override_local_dns: true' '  nameservers:' '    global:' '      - 1.1.1.1' '      - 1.0.0.1' '    split: {}' '  search_domains: []' '  extra_records: []' '' 'unix_socket: /var/run/headscale/headscale.sock' 'unix_socket_permission: \"0770\"' '' 'logtail:' '  enabled: false' '' 'randomize_client_port: false' '' 'taildrop:' '  enabled: true' > /etc/headscale/config.yaml","FROM headscale/headscale:$$cap_headscale_version","USER 0","COPY --from=headscale-config /etc/headscale/config.yaml /etc/headscale/config.yaml","COPY --from=headscale-config /var/run/headscale /var/run/headscale","COPY --from=headscale-config /var/lib/headscale /var/lib/headscale","EXPOSE 8080","CMD [\"serve\"]"]}}},"variables":[{"id":"$$cap_headscale_version","label":"Headscale version","defaultValue":"v0.28.0","description":"Headscale Docker image tag. See https://github.com/juanfont/headscale/releases","validRegex":"/^v?[0-9]+(\\.[0-9]+){2}.*$/"},{"id":"$$cap_public_domain","label":"Public Headscale domain","defaultValue":"$$cap_appname.$$cap_root_domain","description":"Public HTTPS domain for Headscale. This must exactly match the CapRover app domain.","validRegex":"/^[a-zA-Z0-9.-]+$/"},{"id":"$$cap_tailnet_domain","label":"MagicDNS base domain","defaultValue":"tailnet.local","description":"Internal MagicDNS base domain. It must be different from the public Headscale domain.","validRegex":"/^[a-zA-Z0-9.-]+$/"}],"instructions":{"start":"Deploy Headscale, an open-source Tailscale control server, behind CapRover HTTPS.\n\nUse a public HTTPS domain for $$cap_public_domain, then enable HTTPS for this app after deployment.\nThis template uses SQLite storage in a persistent volume and external Tailscale DERP relays.","end":"Headscale is deployed.\n\nNext steps:\n1. Enable HTTPS for https://$$cap_public_domain in CapRover.\n2. Verify https://$$cap_public_domain/health returns OK.\n3. Create a user:\n   docker exec -it <headscale-container> /ko-app/headscale users create pi\n4. Create a preauth key:\n   docker exec -it <headscale-container> /ko-app/headscale preauthkeys create --user pi\n5. Connect a client:\n   tailscale up --login-server https://$$cap_public_domain --auth-key <preauth-key>"},"displayName":"Headscale","isOfficial":true,"description":"Self-hosted Tailscale control server with SQLite persistence.","documentation":"https://headscale.net/stable/setup/install/container/"}
