By default headscale fetches https://controlplane.tailscale.com/derpmap/default at startup and treats failure as fatal, so it cannot boot when that URL is unreachable. A self-hosted control plane that will not start without Tailscale's infrastructure rather misses the point of self-hosting -- and it crash-looped for exactly that reason while neptun had no DNS. Enable the embedded DERP server on region 999 and drop the upstream map. The relay rides Caddy on :443, which is why that vhost already sets flush_interval -1; only STUN needs a port of its own. Verified against headscale 0.28.0 before committing: it starts clean with urls = [], registers "DERP region: {RegionID:999 ...}" pointing at vpn.mgaction.town with DERPPort 443, and brings up STUN. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
54 lines
2.0 KiB
Nix
54 lines
2.0 KiB
Nix
{ ... }:
|
|
|
|
# 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 ];
|
|
}
|