Headscale (the self-hosted Tailscale control plane) runs as a new stack on proxy_net, following the house pattern of syncthing/forgejo/ntfy: own directory, own compose file, committed config, git-ignored bind-mounted state, CLI-only admin via docker exec. - headscale/config.yaml: server_url https://remote.waldson.com.br, MagicDNS with base domain ts.waldson.com.br, embedded DERP relay with the upstream Tailscale DERP map dropped so relayed traffic stays on the droplet, HA subnet-router route failover, SQLite state under /var/lib/headscale. The CLI unix socket is relocated into the data dir so the UID 1000 container can use it. - headscale/compose.yaml: image pinned to the v0.29 minor, runs as 1000:1000, publishes only STUN on 3478/udp; gRPC not exposed. - Caddyfile: remote.waldson.com.br reverse-proxies to headscale-headscale-1:8080. - README: Headscale stack section (deploy, upgrade, user/node CLI runbook), firewall table row for 3478/udp, DNS row for remote.waldson.com.br.
50 lines
2.5 KiB
YAML
50 lines
2.5 KiB
YAML
name: headscale
|
|
|
|
# Self-hosted Headscale coordination server (the open-source Tailscale control
|
|
# plane) for the owner's tailnet, behind the central Caddy proxy at
|
|
# remote.waldson.com.br. The HTTP API and the login/OIDC endpoints are reached
|
|
# only through Caddy on proxy_net; Caddy transparently upgrades the long-lived
|
|
# client control connections, the same as ntfy. The one thing published to the
|
|
# host is STUN on 3478/udp for the embedded DERP relay. Administration is
|
|
# CLI-only via `docker exec ... headscale` (see README.md for the deploy,
|
|
# upgrade, and user/node runbook). The whole posture lives in the committed
|
|
# config.yaml; only SQLite state and generated keys are mutable at runtime.
|
|
|
|
services:
|
|
headscale:
|
|
# Pinned to the current minor series. Headscale is pre-1.0, so the minor is
|
|
# the breaking-change axis: `pull` brings 0.29.x patches while a minor bump
|
|
# (0.30) stays a deliberate tag edit. Headscale trails new Tailscale client
|
|
# capabilities, so review compatibility before bumping the tag - if a client
|
|
# refuses to connect after an upgrade, version skew is the first suspect.
|
|
image: headscale/headscale:v0.29
|
|
# The image entrypoint is `headscale`; the server needs the `serve`
|
|
# subcommand (same shape as ntfy's `serve`).
|
|
command: serve
|
|
restart: unless-stopped
|
|
# Run as the droplet's deploy user so the SQLite db and generated private
|
|
# keys in the bind mount stay owned by 1000 and are manageable over plain
|
|
# SSH, matching the Syncthing and ntfy stacks. Headscale's default unix
|
|
# socket lives under root-only /var/run; config.yaml relocates it into the
|
|
# data dir so this non-root process can create it and the CLI still works.
|
|
user: "1000:1000"
|
|
volumes:
|
|
# Committed posture: server URL, MagicDNS, embedded DERP. Read-only.
|
|
- ./config.yaml:/etc/headscale/config.yaml:ro
|
|
# SQLite database, noise/DERP private keys, and the CLI socket live here on
|
|
# a host bind mount, so the instance survives recreation and is
|
|
# inspectable over SSH. Git-ignored via the repo's data/ rule.
|
|
- ./data:/var/lib/headscale
|
|
ports:
|
|
# STUN for the embedded DERP relay's NAT traversal. The relay itself and
|
|
# every control connection ride Caddy's existing 443; only this UDP port
|
|
# is new on the host firewall. The gRPC remote-CLI port is deliberately
|
|
# not published - admin is via docker exec over the unix socket.
|
|
- "3478:3478/udp"
|
|
mem_limit: 256m
|
|
networks:
|
|
- proxy_net
|
|
|
|
networks:
|
|
proxy_net:
|
|
external: true
|