Skip to main content
Understand the repository structure, configuration files, and data organization for the Headscale + Tailscale Docker Stack.

Repository structure

headscale-tailscale-docker/
├── config/                      # Headscale configuration
│   ├── config.yaml             # Main Headscale configuration
│   └── policy.json             # ACL policies (optional)

├── data/                        # Headscale persistent data
│   ├── db.sqlite               # SQLite database (if using SQLite)
│   ├── private.key             # Server private key
│   └── noise_private.key       # Noise protocol key

├── headplane/                   # Headplane web GUI configuration
│   └── config.yaml             # Headplane settings

├── logs/                        # Application logs
│   └── nginx/                  # nginx access and error logs
│       ├── access.log
│       └── error.log

├── certbot/                     # SSL/TLS certificates
│   ├── conf/                   # Let's Encrypt certificates
│   └── www/                    # ACME challenge files

├── scripts/                     # Helper scripts
│   ├── headscale.sh            # Headscale management wrapper
│   ├── nginx.sh                # nginx management script
│   ├── backup.sh               # Backup automation
│   ├── setup.sh                # Initial setup wizard
│   └── lefthook/               # Git hooks for security

├── tailscale-configs/           # Client configuration templates
│   ├── linux/                  # Linux systemd units
│   ├── macos/                  # macOS LaunchDaemons
│   ├── windows/                # Windows PowerShell scripts
│   ├── docker/                 # Docker sidecar examples
│   └── README.md               # Platform-specific guides

├── docs/                        # Documentation
│   ├── QUICKSTART.md
│   ├── QUICK_START_GUI.md
│   ├── BEST_PRACTICES.md
│   ├── DEPLOYMENT.md
│   ├── NETWORKING.md
│   ├── SECURITY.md
│   ├── GUI_SETUP.md
│   └── NGINX_CONFIGURATION.md

├── docker-compose.yml           # Production service definitions
├── docker-compose.override.example.yml  # Development overrides
├── nginx.conf                   # Production nginx (SSL/TLS)
├── nginx.dev.conf               # Development nginx (HTTP)
├── .env.example                 # Environment variable template
├── .gitignore                   # Git exclusions
├── lefthook.yml                 # Git hooks configuration
├── Caddyfile                    # Alternative Caddy config
├── CLAUDE.md                    # AI assistant guidance
└── README.md                    # Main documentation

Configuration files

Headscale configuration

Location: config/config.yaml Purpose: Main Headscale server configuration including:
  • Server URL and listening addresses
  • Database connection (PostgreSQL or SQLite)
  • DERP server settings
  • DNS and MagicDNS configuration
  • IP address prefixes (IPv4/IPv6)
  • Logging and policy settings
Critical parameters:
server_url: http://localhost:8000  # Must match deployment
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 0.0.0.0:9090

database:
  type: postgres
  postgres:
    host: postgres
    pass: changeme  # Must match .env POSTGRES_PASSWORD
The database password must be synchronized between config.yaml and .env or the stack will fail to start.

ACL policies

Location: config/policy.json Purpose: Access control rules defining:
  • User groups
  • Tag ownership
  • Traffic rules between nodes
  • Auto-approval for routes and exit nodes
  • SSH access policies
Example:
{
  "tagOwners": {
    "tag:server": ["user:admin"],
    "tag:client": ["user:alice", "user:bob"]
  },
  "acls": [
    {
      "action": "accept",
      "src": ["tag:client"],
      "dst": ["tag:server:*"]
    }
  ]
}
See ACL policies documentation for complete syntax.

nginx configuration

Production: nginx.conf
  • SSL/TLS termination with Let’s Encrypt
  • HTTP to HTTPS redirection
  • Rate limiting (3-tier strategy)
  • Security headers (HSTS, CSP, X-Frame-Options)
  • Ports 80 and 443
Development: nginx.dev.conf
  • HTTP only (no SSL)
  • No rate limiting
  • Port 8000
  • Simplified for local testing
Switching: Use docker-compose.override.yml to select dev config.

Docker Compose

Production: docker-compose.yml Defines 5 services:
  1. headscale - Control plane server
  2. nginx - Reverse proxy
  3. headplane - Web GUI
  4. postgres - Database
  5. certbot - SSL certificate management
Development override: docker-compose.override.example.yml Modifies production config for local dev:
  • Changes nginx config to nginx.dev.conf
  • Switches port from 80/443 to 8000
  • Simplifies for HTTP-only testing

Environment variables

Location: .env (create from .env.example) Variables:
# Domain for production deployment
HEADSCALE_DOMAIN=headscale.example.com

# PostgreSQL credentials
POSTGRES_DB=headscale
POSTGRES_USER=headscale
POSTGRES_PASSWORD=changeme  # CHANGE THIS

# Timezone
TZ=UTC

# Headplane authentication
HEADPLANE_API_KEY=your-api-key-here
HEADPLANE_COOKIE_SECRET=exactly-32-characters-required!
HEADPLANE_COOKIE_SECRET must be exactly 32 characters or Headplane will fail to start.

Data directories

Headscale data

Location: data/ Contents:
  • db.sqlite - Local SQLite database (if not using PostgreSQL)
  • private.key - Server’s WireGuard private key
  • noise_private.key - Noise protocol encryption key
Backup importance: CRITICAL These keys are essential for:
  • Decrypting existing connections
  • Maintaining node registrations
  • Preserving network state
Backup command:
tar -czf headscale-data-backup-$(date +%Y%m%d).tar.gz data/

PostgreSQL data

Location: Docker volume postgres-data Contents:
  • User accounts
  • Node registrations
  • Pre-auth keys
  • Route advertisements
  • Audit logs
Backup:
# Dump database
docker exec headscale-db pg_dump -U headscale headscale > backup.sql

# Restore
cat backup.sql | docker exec -i headscale-db psql -U headscale
See Backup & Restore for complete procedures.

SSL certificates

Location: certbot/conf/ Contents:
  • live/<domain>/fullchain.pem - Certificate + intermediate chain
  • live/<domain>/privkey.pem - Private key
  • archive/<domain>/ - All certificate versions
  • renewal/<domain>.conf - Renewal configuration
Auto-renewal: Certbot container checks every 12 hours. Manual renewal:
docker compose exec certbot certbot renew
docker compose restart nginx

Logs

nginx logs: logs/nginx/
  • access.log - All HTTP requests with timing
  • error.log - nginx errors and warnings
Container logs: Via Docker
# View all logs
docker compose logs -f

# Specific service
docker compose logs -f headscale

# Last 100 lines
docker compose logs --tail=100 headscale

Helper scripts

headscale.sh

Location: scripts/headscale.sh Purpose: Wrapper for common Headscale operations Usage:
./scripts/headscale.sh users create alice
./scripts/headscale.sh keys create alice --reusable --expiration 24h
./scripts/headscale.sh nodes list
./scripts/headscale.sh routes enable --route-id 1
See headscale.sh reference for all commands.

nginx.sh

Location: scripts/nginx.sh Purpose: nginx management and diagnostics Usage:
./scripts/nginx.sh status
./scripts/nginx.sh logs
./scripts/nginx.sh ssl-info
./scripts/nginx.sh test
See nginx.sh reference for complete documentation.

backup.sh

Location: scripts/backup.sh Purpose: Automated backup of PostgreSQL, config, and data Usage:
# Manual backup
./scripts/backup.sh

# Automated (cron)
0 2 * * * /path/to/scripts/backup.sh
See backup.sh reference for details.

Client configuration templates

Location: tailscale-configs/ Platform-specific configuration files and setup scripts:

Linux

Files: tailscale-configs/linux/
  • tailscale-headscale.service - systemd unit
  • setup.sh - Automated installation script
Usage:
cd tailscale-configs/linux
sudo ./setup.sh

macOS

Files: tailscale-configs/macos/
  • com.tailscale.headscale.plist - LaunchDaemon
  • install.sh - Installation script

Windows

Files: tailscale-configs/windows/
  • connect.ps1 - PowerShell connection script
  • setup.ps1 - Automated setup

Docker

Files: tailscale-configs/docker/
  • docker-compose.example.yml - Sidecar pattern example
  • Dockerfile.tailscale - Custom image with Tailscale

Git configuration

.gitignore

Excludes sensitive and generated files:
.env
data/
logs/
certbot/
docker-compose.override.yml
postgres-data/

lefthook.yml

Purpose: Git hooks for security Prevents committing:
  • .env files with credentials
  • .DS_Store macOS artifacts
  • Database files
  • Private keys
Setup:
brew install lefthook
lefthook install
See Lefthook documentation for details.

File permissions

Important permissions

Private keys (data/private.key, data/noise_private.key):
chmod 600 data/*.key
Configuration files:
chmod 644 config/config.yaml
chmod 644 config/policy.json
Scripts:
chmod +x scripts/*.sh
SSL certificates:
chmod 600 certbot/conf/live/*/privkey.pem
chmod 644 certbot/conf/live/*/fullchain.pem
Never commit private keys or .env files to version control. Use .gitignore to prevent accidental exposure.

Development vs production

Development files

Active:
  • nginx.dev.conf (HTTP only)
  • docker-compose.override.yml (port 8000)
  • .env with localhost settings
server_url: http://localhost:8000

Production files

Active:
  • nginx.conf (SSL/TLS)
  • docker-compose.yml (ports 80, 443)
  • .env with public domain
  • certbot/ with Let’s Encrypt certificates
server_url: https://yourdomain.com

Maintenance

Regular tasks

Daily:
  • Review logs/nginx/access.log for unusual activity
  • Check docker compose ps for service health
Weekly:
  • Run backup script
  • Check disk usage: du -sh data/ certbot/ logs/
  • Review expired pre-auth keys
Monthly:
  • Update Docker images: docker compose pull && docker compose up -d
  • Clean old logs: find logs/ -mtime +30 -delete
  • Test backup restoration

Cleanup

Remove old backups:
find . -name "headscale-backup-*.tar.gz" -mtime +30 -delete
Clean Docker:
docker system prune -a
Expire offline nodes:
docker exec headscale headscale nodes expire --all-offline

Architecture

System architecture and components

Configuration

Detailed configuration reference

Backup & Restore

Data protection procedures

Security

Security best practices

Build docs developers (and LLMs) love