diff --git a/headscale_dev/README.md b/headscale_dev/README.md new file mode 100644 index 0000000..b9b3fd1 --- /dev/null +++ b/headscale_dev/README.md @@ -0,0 +1,105 @@ +# Heascale + +## Overview + +Headscale is a self-hosted control server for Tailscale clients. It allows you to run your own private coordination plane for a WireGuard-based mesh network without relying on external SaaS services. + +This deployment is designed to run fully self-hosted behind a reverse proxy. + +Requires a valid config.yaml before first startup. + +## Project Details + +* Project Repository: https://github.com/juanfont/headscale +* Container Image: https://hub.docker.com/r/headscale/headscale +* Admin UI Image: https://hub.docker.com/r/goodieshq/headscale-admin +* Documentation: https://headscale.net/ +* Reverse Proxy Domain: headscale.example.com +* Internal Service Port: 8080 (not exposed publicly) +* Environment Variable Notes + +## Core Settings + +These variables are used in the Docker Compose file (.env): + +``` +HEADSCALE_IMAGE – Docker image for Headscale server (default: headscale/headscale:latest) +HEADSCALE_UI_IMAGE – Docker image for admin UI +HEADSCALE_RESTART – Restart policy (default: unless-stopped) +HEADSCALE_UI_RESTART – Restart policy for UI container + +# Paths +CONFIG_PATH – Location of configuration files (config.yaml) +DATA_PATH – Persistent database storage (SQLite) +LOG_PATH – Application logs + +# System +TZ – Timezone (e.g. America/Vancouver) +HEADSCALE_LOG_LEVEL – Logging verbosity (info, debug, etc.) +Domain Configuration +HEADSCALE_DOMAIN – Public-facing domain used via reverse proxy +Example: headscale.4lt.ca + +HEADSCALE_URL – Full URL used by Headscale clients +Must match your reverse proxy domain: +``` + +## Volume Notes + +All data is stored locally on the host for full self-sovereignty: + +``` +./config/headscale/config → Contains config.yaml (required configuration file) +./data/headscale → SQLite database and persistent Headscale state +./logs/headscale → Optional logs for debugging and monitoring +``` + +Back up data/ and config/ regularly for recovery. + +## Network Notes + +Uses a private Docker bridge network: internal +No public ports exposed directly +All access is routed through reverse proxy (NPM) +Reverse Proxy Requirements (Nginx Proxy Manager) + +For headscale.example: + +Forward hostname: headscale +Forward port: 8080 +Scheme: http +Enable: +Websockets Support ✔ +Block Common Exploits ✔ +SSL (Let’s Encrypt) ✔ +Disable caching ✔ +Additional Notes / Gotchas +Headscale will not function without a valid config.yaml + +server_url inside config.yaml MUST match your domain. + +Clients must explicitly point to your server: + +tailscale up --login-server=https://headscale.4lt.ca +Do NOT expose port 8080 publicly — only via reverse proxy +SQLite is used by default (no external DB required) +Dockhand Stack, Deploy from Git + +Cookbooks Repository +stackname: headscale + +Compose file path: + +headscale/compose.yaml + +Additional env file (optional): + +headscale/sample.env +Deployment Steps +Load sample.env into Dockhand environment variables + +Ensure config.yaml exists in: + +./config/headscale/config/config.yaml +Deploy stack +Configure reverse proxy in Nginx Proxy Manager diff --git a/headscale_dev/compse.yaml b/headscale_dev/compse.yaml new file mode 100644 index 0000000..83a9680 --- /dev/null +++ b/headscale_dev/compse.yaml @@ -0,0 +1,51 @@ +# Headscale Docker Compose Template + +services: + headscale: + image: ${HEADSCALE_IMAGE:-headscale/headscale:latest} + restart: ${HEADSCALE_RESTART:-unless-stopped} + + volumes: + - ${CONFIG_PATH:-./config}/headscale/config:/etc/headscale + - ${DATA_PATH:-./data}/headscale:/var/lib/headscale + - ${LOG_PATH:-./logs}/headscale:/var/log/headscale + - /etc/localtime:/etc/localtime:ro + + environment: + - TZ=${TZ:-UTC} + - HEADSCALE_LOG_LEVEL=${HEADSCALE_LOG_LEVEL:-info} + + expose: + - "8080" + + networks: + - internal + + command: + - headscale + - serve + + user: "1000:1000" + + headscale-ui: + image: ${HEADSCALE_UI_IMAGE:-goodieshq/headscale-admin:latest} + restart: ${HEADSCALE_UI_RESTART:-unless-stopped} + + environment: + - TZ=${TZ:-UTC} + - HEADSCALE_URL=http://headscale:8080 + + expose: + - "80" + + depends_on: + - headscale + + networks: + - internal + +networks: + proxy: + external: true + internal: + driver: bridge \ No newline at end of file diff --git a/headscale_dev/config.yaml b/headscale_dev/config.yaml new file mode 100644 index 0000000..3e7157b --- /dev/null +++ b/headscale_dev/config.yaml @@ -0,0 +1,12 @@ +server_url: https://headscale.example.com +listen_addr: 0.0.0.0:8080 + +db_type: sqlite3 +db_path: /var/lib/headscale/db.sqlite + +log: + level: info + +dns_config: + override_local_dns: true + base_domain: tailnet.local \ No newline at end of file diff --git a/headscale_dev/sample.env b/headscale_dev/sample.env new file mode 100644 index 0000000..c4391b8 --- /dev/null +++ b/headscale_dev/sample.env @@ -0,0 +1,30 @@ +# ========================= +# Headscale Configuration +# ========================= + +# Images +HEADSCALE_IMAGE=headscale/headscale:latest +HEADSCALE_UI_IMAGE=goodieshq/headscale-admin:0.26 + +# Restart policy +HEADSCALE_RESTART=unless-stopped +HEADSCALE_UI_RESTART=unless-stopped + +# Paths +CONFIG_PATH=./config +DATA_PATH=./data +LOG_PATH=./logs + +# Timezone +TZ=America/Vancouver + +# Logging +HEADSCALE_LOG_LEVEL=info + +# Domain (IMPORTANT) +HEADSCALE_DOMAIN=headscale.example.com +HEADSCALE_URL=https://headscale.example.com + +# Ports (not exposed publicly, but useful for internal reference) +HEADSCALE_PORT=8080 +HEADSCALE_UI_PORT=9090 \ No newline at end of file