feat(headscale): add self-hosted Tailscale control server stack
CLI-managed (no web UI), sqlite-backed, fronted by Traefik like the other stacks. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ Each stack is independently deployable with its own `compose.yml` and `.env`. Al
|
|||||||
| `coolify` | Deployment platform | coolify, realtime, redis, db |
|
| `coolify` | Deployment platform | coolify, realtime, redis, db |
|
||||||
| `passbolt` | Password manager (GPG-encrypted, team sharing) | passbolt, db |
|
| `passbolt` | Password manager (GPG-encrypted, team sharing) | passbolt, db |
|
||||||
| `code` | Browser-based VS Code IDE with Anthropic API access | code |
|
| `code` | Browser-based VS Code IDE with Anthropic API access | code |
|
||||||
|
| `headscale` | Self-hosted Tailscale control server (WireGuard mesh VPN) | headscale |
|
||||||
|
|
||||||
## Config files
|
## Config files
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
TRAEFIK_HOST=headscale.example.com
|
||||||
|
NETWORK_NAME=falcon_network
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
services:
|
||||||
|
headscale:
|
||||||
|
image: headscale/headscale:latest
|
||||||
|
container_name: headscale
|
||||||
|
command: ["serve"]
|
||||||
|
environment:
|
||||||
|
TZ: ${TIMEZONE:-Europe/Amsterdam}
|
||||||
|
HEADSCALE_SERVER_URL: https://${TRAEFIK_HOST}
|
||||||
|
volumes:
|
||||||
|
- ./config.yaml:/etc/headscale/config.yaml:ro
|
||||||
|
- ../.data/headscale/data:/var/lib/headscale
|
||||||
|
- ../.data/headscale/run:/var/run/headscale
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/ko-app/headscale", "health"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 10s
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.middlewares.headscale-redirect-web-secure.redirectscheme.scheme=https"
|
||||||
|
- "traefik.http.routers.headscale-web.middlewares=headscale-redirect-web-secure"
|
||||||
|
- "traefik.http.routers.headscale-web.rule=Host(`${TRAEFIK_HOST}`)"
|
||||||
|
- "traefik.http.routers.headscale-web.entrypoints=web"
|
||||||
|
- "traefik.http.routers.headscale-web-secure.rule=Host(`${TRAEFIK_HOST}`)"
|
||||||
|
- "traefik.http.routers.headscale-web-secure.tls.certresolver=resolver"
|
||||||
|
- "traefik.http.routers.headscale-web-secure.entrypoints=web-secure"
|
||||||
|
- "traefik.http.routers.headscale-web-secure.middlewares=security-headers@file,no-index@file"
|
||||||
|
- "traefik.http.services.headscale-web-secure.loadbalancer.server.port=8080"
|
||||||
|
- "traefik.docker.network=${NETWORK_NAME}"
|
||||||
|
networks:
|
||||||
|
- compose_network
|
||||||
|
networks:
|
||||||
|
compose_network:
|
||||||
|
name: ${NETWORK_NAME}
|
||||||
|
external: true
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# Base Headscale config. Host-specific values (server_url, listen addrs) are
|
||||||
|
# overridden via HEADSCALE_* environment variables in compose.yml, following
|
||||||
|
# headscale's viper env-binding (HEADSCALE_SERVER_URL overrides server_url, etc).
|
||||||
|
# Full reference: https://github.com/juanfont/headscale/blob/main/config-example.yaml
|
||||||
|
|
||||||
|
server_url: https://headscale.example.com
|
||||||
|
listen_addr: 0.0.0.0:8080
|
||||||
|
metrics_listen_addr: 127.0.0.1:9090
|
||||||
|
grpc_listen_addr: 127.0.0.1:50443
|
||||||
|
grpc_allow_insecure: false
|
||||||
|
|
||||||
|
noise:
|
||||||
|
private_key_path: /var/lib/headscale/noise_private.key
|
||||||
|
|
||||||
|
prefixes:
|
||||||
|
v4: 100.64.0.0/10
|
||||||
|
v6: fd7a:115c:a1e0::/48
|
||||||
|
allocation: sequential
|
||||||
|
|
||||||
|
derp:
|
||||||
|
server:
|
||||||
|
enabled: false
|
||||||
|
urls:
|
||||||
|
- https://controlplane.tailscale.com/derpmap/default
|
||||||
|
paths: []
|
||||||
|
auto_update_enabled: true
|
||||||
|
update_frequency: 24h
|
||||||
|
|
||||||
|
disable_check_updates: false
|
||||||
|
|
||||||
|
node:
|
||||||
|
ephemeral:
|
||||||
|
inactivity_timeout: 30m
|
||||||
|
|
||||||
|
database:
|
||||||
|
type: sqlite
|
||||||
|
sqlite:
|
||||||
|
path: /var/lib/headscale/db.sqlite
|
||||||
|
write_ahead_log: true
|
||||||
|
|
||||||
|
unix_socket: /var/run/headscale/headscale.sock
|
||||||
|
unix_socket_permission: "0770"
|
||||||
|
|
||||||
|
log:
|
||||||
|
format: text
|
||||||
|
level: info
|
||||||
|
|
||||||
|
policy:
|
||||||
|
mode: file
|
||||||
|
path: ""
|
||||||
|
|
||||||
|
dns:
|
||||||
|
magic_dns: true
|
||||||
|
base_domain: hs.internal
|
||||||
|
nameservers:
|
||||||
|
global:
|
||||||
|
- 1.1.1.1
|
||||||
|
- 1.0.0.1
|
||||||
|
search_domains: []
|
||||||
|
|
||||||
|
# TLS is terminated by Traefik; headscale itself serves plain HTTP internally.
|
||||||
|
tls_letsencrypt_hostname: ""
|
||||||
|
tls_cert_path: ""
|
||||||
|
tls_key_path: ""
|
||||||
Reference in New Issue
Block a user