# Headscale coordination server for the owner's self-hosted tailnet, reached at # https://remote.waldson.com.br through the central Caddy proxy. This file is # the committed posture: MagicDNS, the embedded DERP relay, and the SQLite data # layout are all reproduced on every recreate instead of depending on runtime # state. Only the SQLite database and the auto-generated private keys (under # /var/lib/headscale, the git-ignored bind mount) are mutable at runtime. # # Schema tracks the pinned image (headscale/headscale:v0.29). When bumping the # minor tag, diff this against that release's config-example.yaml: Headscale's # config schema changes between minors. # Public login URL every client connects to. Caddy terminates TLS for this # hostname and reverse-proxies to listen_addr below. DERP requires https here. server_url: https://remote.waldson.com.br # Bind on all interfaces inside the container so Caddy can reach it by name on # proxy_net (headscale-headscale-1:8080). The port is never published to the # host; only Caddy on 80/443 fronts it, the same shape as the other stacks. listen_addr: 0.0.0.0:8080 # Metrics and gRPC stay bound to loopback inside the container and are never # published. Administration is done with `docker exec ... headscale` over the # unix socket below, so the gRPC remote-CLI surface is not exposed. metrics_listen_addr: 127.0.0.1:9090 grpc_listen_addr: 127.0.0.1:50443 grpc_allow_insecure: false # Caddy is the only thing in front of Headscale; trust its forwarding headers so # logs and rate limiting see the real client IP. Caddy reaches Headscale from # its own container on proxy_net, so trust that private range. trusted_proxies: - 172.16.0.0/12 noise: private_key_path: /var/lib/headscale/noise_private.key # Tailnet address pools. Left at the standard Tailscale CGNAT/ULA ranges. prefixes: v4: 100.64.0.0/10 v6: fd7a:115c:a1e0::/48 allocation: sequential # Embedded DERP relay: run our own relay and STUN so even traffic that cannot go # direct is relayed through this droplet, never through Tailscale Inc. The # upstream DERP map is dropped (urls: [] and no auto-update) so the tailnet uses # only this region. derp: server: enabled: true region_id: 999 region_code: "waldson" region_name: "Waldson Embedded DERP" verify_clients: true # STUN for NAT traversal. Published on the host as 3478/udp (the one new # firewall row this stack adds); mandatory when the embedded DERP is on. stun_listen_addr: "0.0.0.0:3478" private_key_path: /var/lib/headscale/derp_server_private.key automatically_add_embedded_derp_region: true # Droplet public IPv4 so clients can reach this DERP region directly for # better connection stability. No IPv6 line: the droplet has no stable # public IPv6, and advertising a bogus one would send clients to a dead # address. ipv4: 174.138.36.232 # No external DERP maps: relayed traffic must stay on our own infrastructure. urls: [] paths: [] auto_update_enabled: false update_frequency: 3h disable_check_updates: false node: expiry: 0 ephemeral: inactivity_timeout: 30m # HA subnet-router health probing. Kept on so that when the Home Assistant # box (primary) and mercurio (failover) both advertise 192.168.0.0/24, the # primary role fails over to whichever is healthy. This is the route-failover # support the tailnet topology depends on. routes: ha: probe_interval: 10s probe_timeout: 5s 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 is terminated by Caddy, so Headscale serves plain HTTP on listen_addr and # manages no certificates of its own. tls_letsencrypt_hostname: "" tls_letsencrypt_challenge_type: HTTP-01 tls_cert_path: "" tls_key_path: "" log: level: info format: text policy: # Allow-all within the tailnet: single-user fleet, the tailnet boundary is the # security boundary. An empty path keeps the built-in allow-all default. mode: file path: "" # MagicDNS so devices reach each other by name (ssh mercurio). The base domain # is deliberately different from server_url's host, which Headscale requires, # and exists only inside the tailnet (no public records for it). dns: magic_dns: true base_domain: ts.waldson.com.br 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: [] # CLI connects over this unix socket with no auth. Relocated from the default # /var/run/headscale (root-owned, not writable by our non-root container) into # the bind-mounted data dir, so the UID 1000 process can create it and # `docker exec ... headscale` works. unix_socket: /var/lib/headscale/headscale.sock unix_socket_permission: "0770" logtail: enabled: false taildrop: enabled: true auto_update: enabled: false