84 lines
3.6 KiB
YAML
84 lines
3.6 KiB
YAML
# Standalone DERP Relay Server — Atlantis (Home NAS)
|
|
# =============================================================================
|
|
# Tailscale/Headscale DERP relay for home-network fallback connectivity.
|
|
# Serves as region 902 "Home - Atlantis" in the headscale derpmap.
|
|
#
|
|
# Why standalone (not behind nginx):
|
|
# The DERP protocol does an HTTP→binary protocol switch inside TLS.
|
|
# It is incompatible with HTTP reverse proxies. Must handle TLS directly.
|
|
#
|
|
# Port layout:
|
|
# 8445/tcp — DERP relay (direct TLS, NOT proxied through NPM)
|
|
# 3480/udp — STUN (NAT traversal hints)
|
|
# Port 3478 taken by coturn/Jitsi, 3479 taken by coturn/Matrix on matrix-ubuntu.
|
|
#
|
|
# TLS cert:
|
|
# Issued by Let's Encrypt via certbot DNS challenge (Cloudflare).
|
|
# Cert path: /volume1/docker/derper-atl/certs/
|
|
# Cloudflare credentials: /volume1/docker/derper-atl/secrets/cloudflare.ini
|
|
# Auto-renewed monthly by the cert-renewer sidecar (ofelia + certbot/dns-cloudflare).
|
|
# On first deploy or manual renewal, run:
|
|
# docker run -it --rm \
|
|
# -v /volume1/docker/derper-atl/certs:/etc/letsencrypt \
|
|
# -v /volume1/docker/derper-atl/secrets:/root/.secrets:ro \
|
|
# certbot/dns-cloudflare certonly \
|
|
# --dns-cloudflare \
|
|
# --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
|
|
# -d derp-atl.vish.gg
|
|
# Then copy certs to flat layout:
|
|
# cp certs/live/derp-atl.vish.gg/fullchain.pem certs/live/derp-atl.vish.gg/derp-atl.vish.gg.crt
|
|
# cp certs/live/derp-atl.vish.gg/privkey.pem certs/live/derp-atl.vish.gg/derp-atl.vish.gg.key
|
|
#
|
|
# Firewall / DSM rules required (one-time):
|
|
# Allow inbound 8445/tcp and 3480/udp in DSM → Security → Firewall
|
|
#
|
|
# Router port forwards required (one-time, on home router):
|
|
# 8445/tcp → 192.168.0.200 (Atlantis LAN IP, main interface)
|
|
# 3480/udp → 192.168.0.200
|
|
#
|
|
# DNS: derp-atl.vish.gg → home public IP (managed by dynamicdnsupdater.yaml, unproxied)
|
|
# =============================================================================
|
|
|
|
services:
|
|
derper-atl:
|
|
image: fredliang/derper:latest
|
|
container_name: derper-atl
|
|
restart: unless-stopped
|
|
ports:
|
|
- "8445:8445" # DERP TLS — direct, not behind NPM
|
|
- "3480:3480/udp" # STUN (3478 taken by coturn/Jitsi, 3479 taken by coturn/Matrix)
|
|
volumes:
|
|
# Full letsencrypt mount required — live/ contains symlinks into archive/
|
|
# mounting only live/ breaks symlink resolution inside the container
|
|
- /volume1/docker/derper-atl/certs:/etc/letsencrypt:ro
|
|
environment:
|
|
- DERP_DOMAIN=derp-atl.vish.gg
|
|
- DERP_CERT_MODE=manual
|
|
- DERP_CERT_DIR=/etc/letsencrypt/live/derp-atl.vish.gg
|
|
- DERP_ADDR=:8445
|
|
- DERP_STUN=true
|
|
- DERP_STUN_PORT=3480
|
|
- DERP_HTTP_PORT=-1 # disable plain HTTP, TLS only
|
|
- DERP_VERIFY_CLIENTS=false # allow any node (headscale manages auth)
|
|
|
|
cert-renewer:
|
|
# Runs certbot monthly via supercronic; after renewal copies certs to the
|
|
# flat layout derper expects, then restarts derper-atl via Docker socket.
|
|
# Schedule: 03:00 on the 1st of every month.
|
|
image: certbot/dns-cloudflare:latest
|
|
container_name: derper-atl-cert-renewer
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- derper-atl
|
|
entrypoint: >-
|
|
sh -c "
|
|
apk add --no-cache supercronic curl &&
|
|
echo '0 3 1 * * /renew.sh' > /crontab &&
|
|
exec supercronic /crontab
|
|
"
|
|
volumes:
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
- /volume1/docker/derper-atl/certs:/etc/letsencrypt
|
|
- /volume1/docker/derper-atl/secrets:/root/.secrets:ro
|
|
- /volume1/docker/derper-atl/renew.sh:/renew.sh:ro
|