{ ... }: # Headscale — self-hosted control server for the tailnet. Every host's # services/vpn/tailscale.nix points --login-server at https://vpn.mgaction.town # (this host). MagicDNS base_domain "hosts.mgaction.town" matches the # "jupiter.hosts.mgaction.town" names used in this repo's Caddy vhosts # (e.g. hosts/neptun/configuration.nix) — don't change one without the other. # # TLS terminates at Caddy (see the host's configuration.nix); headscale # itself only listens on localhost. { services.headscale = { enable = true; port = 8082; # off the default 8080 to stay clear of other web apps settings = { server_url = "https://vpn.mgaction.town"; dns = { base_domain = "hosts.mgaction.town"; nameservers.global = [ "1.1.1.1" "9.9.9.9" ]; }; # Run our own DERP relay instead of pulling Tailscale's map. # # With the default (urls = [controlplane.tailscale.com/derpmap/default], # auto_update_enabled = true) headscale fetches that map at startup and # treats failure as FATAL — so a DNS blip or a Tailscale outage stops the # control server from booting at all. A self-hosted control plane that # can't start without Tailscale's infrastructure rather misses the point. # # The relay itself rides Caddy on :443 (hence the flush_interval -1 on # that vhost); only STUN needs its own UDP port. derp = { urls = [ ]; auto_update_enabled = false; server = { enabled = true; region_id = 999; # 900-999 is the custom range region_code = "neptun"; region_name = "neptun"; stun_listen_addr = "0.0.0.0:3478"; automatically_add_embedded_derp_region = true; }; }; }; }; # STUN for the embedded DERP server above. Also needs a matching inbound-UDP # rule in netcup's edge firewall — it is stateless and defaults to denying # inbound UDP outright, which silently kills every DNS/NTP reply too. networking.firewall.allowedUDPPorts = [ 3478 ]; }