Skip to content

organicnz/headscale-tailscale-docker

Repository files navigation

Headscale + Tailscale Docker Compose Stack

A production-ready Docker Compose setup for running your own Headscale server - an open-source, self-hosted implementation of the Tailscale control server.

βœ… Current Status

All services are running and operational:

  • βœ… Headscale v0.27.0 - Running with SQLite database
  • βœ… Headplane Web GUI - Accessible at http://localhost:3001/admin/
  • βœ… nginx Reverse Proxy - HTTP proxy on port 8000
  • βœ… Health Check - Passing at http://localhost:8000/health
  • βœ… API Key - Generated and configured
  • βœ… ACL Policies - Tag-based security configured
  • βœ… Helper Scripts - Ready to use

Ready to connect devices!


🎨 NEW: GUI-Only Quick Start!

Don't want to use command line?

πŸ‘‰ See QUICK_START_GUI.md for step-by-step GUI guide:

  1. Open Headplane web interface β†’ Generate pre-auth key
  2. Download Tailscale app on your device
  3. Configure custom server in the app
  4. Connect with the key
  5. Done! No terminal needed! πŸŽ‰

Perfect for: Windows users, Mac users, mobile devices, anyone who prefers graphical interfaces

✨ Features

  • Headscale v0.27.0 - Pinned version with SQLite database
  • Headplane Web GUI - Modern web interface for management
  • nginx Reverse Proxy - HTTP/HTTPS support
  • Best Practices - Security-focused configuration with ACL policies
  • Tag-Based ACLs - Organized network access control
  • Helper Scripts - Easy CLI management

πŸ“‹ Prerequisites

  • Docker and Docker Compose installed
  • For local development: Create docker-compose.override.yml from example
  • For production: A domain name with DNS pointing to your server and ports 80/443 open
  • Recommended: Lefthook for Git hooks (prevents committing secrets)
    # macOS
    brew install lefthook
    
    # After clone
    lefthook install
    See LEFTHOOK.md for details

🌐 Access Points

Once running, you can access:

Generate API Key: docker exec headscale headscale apikeys create

🎨 Want to Use GUIs Instead of Command Line?

See GUI_SETUP.md for complete guide on:

  • Using Headplane web interface for server management
  • Using Tailscale desktop apps (Windows, Mac, Linux)
  • Using Tailscale mobile apps (iOS, Android)
  • No command-line needed!

πŸš€ Quick Start (Local Development)

This setup is ready to run locally on http://localhost:8000

1. Start the Stack

docker compose up -d

Check logs:

docker compose logs -f

Verify health:

curl http://localhost:8000/health
# Should return: {"status":"pass"}

2. Access Headplane Web GUI

Open your browser to:

http://localhost:3001/admin/

Login with API Key:

Generate an API key:

docker exec headscale headscale apikeys create --expiration 999d

Copy the key and paste it into the Headplane login page.

3. Create Your First User

docker exec headscale headscale users create myuser

Or use the helper script:

./scripts/headscale.sh users create myuser

4. Generate a Pre-Auth Key

Create a reusable pre-auth key for connecting devices:

docker exec headscale headscale preauthkeys create --user 1 --reusable --expiration 24h

Or with helper script:

./scripts/headscale.sh keys create myuser --reusable --expiration 24h

Save this key - you'll need it to connect devices.

πŸ”— Connecting Devices

Quick Connection

Generate a pre-auth key:

docker exec headscale headscale preauthkeys create --user 1 --reusable --expiration 24h

Connect any device:

# Linux/macOS
sudo tailscale up --login-server http://localhost:8000 --authkey YOUR_KEY --accept-routes

# Windows (PowerShell as Admin)
tailscale up --login-server http://localhost:8000 --authkey YOUR_KEY --accept-routes

πŸ“¦ Using Configuration Files

For easier setup, use the pre-made configuration files in tailscale-configs/:

  • Linux (systemd): Automated setup script
  • macOS: LaunchDaemon for auto-start
  • Windows: PowerShell script
  • Docker: Docker Compose sidecar pattern

See tailscale-configs/README.md for complete documentation.

Quick start:

cd tailscale-configs
# Choose your platform and follow the README

Windows

  1. Download Tailscale from https://tailscale.com/download
  2. Install and open Tailscale
  3. Open CMD as Administrator and run:
tailscale up --login-server https://headscale.yourdomain.com --authkey YOUR_PREAUTH_KEY

Docker Container

Add this to any Docker Compose service:

services:
  myapp:
    image: myapp:latest
    network_mode: "service:tailscale"
    depends_on:
      - tailscale

  tailscale:
    image: tailscale/tailscale:latest
    hostname: myapp-container
    environment:
      - TS_AUTHKEY=YOUR_PREAUTH_KEY
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_LOGIN_SERVER=https://headscale.yourdomain.com
    volumes:
      - tailscale-data:/var/lib/tailscale
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    restart: unless-stopped

volumes:
  tailscale-data:

Management Commands

User Management

List all users:

docker exec headscale headscale users list

Create a new user:

docker exec headscale headscale users create USERNAME

Delete a user:

docker exec headscale headscale users destroy USERNAME

Node Management

List all nodes:

docker exec headscale headscale nodes list

Register a node (if using manual registration):

docker exec headscale headscale nodes register --user USERNAME --key NODEKEY

Delete a node:

docker exec headscale headscale nodes delete --identifier NODE_ID

Pre-Auth Keys

List all pre-auth keys:

docker exec headscale headscale preauthkeys list --user USERNAME

Create a reusable key that expires in 1 week:

docker exec headscale headscale preauthkeys create --user USERNAME --reusable --expiration 168h

Create a single-use ephemeral key:

docker exec headscale headscale preauthkeys create --user USERNAME --ephemeral

Expire a pre-auth key:

docker exec headscale headscale preauthkeys expire --user USERNAME --key KEY

Routes

List all routes:

docker exec headscale headscale routes list

Enable a route:

docker exec headscale headscale routes enable --route-id ROUTE_ID

Advertise subnet routes (on the client):

sudo tailscale up --advertise-routes=10.0.0.0/24 --login-server https://headscale.yourdomain.com

Advanced Configuration

ACL Policies

Create an ACL policy file to control traffic between nodes:

nano config/acl.json

Example ACL:

{
  "acls": [
    {
      "action": "accept",
      "src": ["*"],
      "dst": ["*:*"]
    }
  ]
}

Update config/config.yaml:

acl_policy_path: /etc/headscale/acl.json

Restart Headscale:

docker compose restart headscale

Custom DERP Servers

To use your own DERP server, edit config/config.yaml:

derp:
  server:
    enabled: true
    region_id: 999
    region_code: "home"
    region_name: "Home DERP"
    stun_listen_addr: "0.0.0.0:3478"

  urls: []

MagicDNS

MagicDNS is enabled by default. Configure custom DNS in config/config.yaml:

dns:
  magic_dns: true
  base_domain: yourdomain.net
  nameservers:
    global:
      - 1.1.1.1
      - 8.8.8.8

πŸ’Ύ Backup and Restore

Backup (SQLite)

# Stop Headscale first to ensure consistent backup
docker compose stop headscale

# Backup everything (database + configuration)
tar -czf headscale-backup-$(date +%Y%m%d).tar.gz config/ data/ headplane/

# Restart Headscale
docker compose start headscale

Restore

# Stop services
docker compose down

# Restore backup
tar -xzf headscale-backup-YYYYMMDD.tar.gz

# Restart services
docker compose up -d

Note: For production with PostgreSQL, see BEST_PRACTICES.md for database-specific backup procedures.

πŸ” Troubleshooting

Check Service Status

docker compose ps
docker compose logs -f headscale
docker compose logs -f headplane

Headplane 404 Error

If you get a 404, make sure you're accessing the correct path:

βœ… http://localhost:3001/admin/  (with trailing slash)
❌ http://localhost:3001          (wrong - will 404)

Connection Issues

Test Headscale health:

curl http://localhost:8000/health
# Should return: {"status":"pass"}

Check if nodes can reach server:

sudo tailscale status
sudo tailscale netcheck

Headplane Won't Load

  1. Check if API key is configured in headplane/config.yaml
  2. Verify Headscale is running: curl http://localhost:8000/health
  3. Check Headplane logs: docker logs headplane --tail 50
  4. Ensure cookie_secret is exactly 32 characters

Database Issues

Check SQLite database:

# Verify database file exists
ls -lh data/db.sqlite

# Check database size
du -h data/db.sqlite

# View tables (from within container)
docker exec headscale sqlite3 /var/lib/headscale/db.sqlite ".tables"

Maintenance

Update Services

docker compose pull
docker compose up -d

View Metrics

Headscale exposes Prometheus metrics on port 8080 (localhost only):

curl http://localhost:8080/metrics

Cleanup Old Data

Remove expired pre-auth keys and offline nodes:

docker exec headscale headscale nodes expire --all-offline

Security Recommendations

  1. Use strong passwords - Change the default PostgreSQL password
  2. Limit pre-auth key lifetime - Use short expiration times
  3. Enable ACLs - Implement least-privilege access
  4. Regular updates - Keep Docker images updated
  5. Monitor logs - Check logs regularly for suspicious activity
  6. Backup regularly - Automate database backups
  7. Use Git hooks - Install Lefthook to prevent committing secrets (see LEFTHOOK.md)

πŸ—οΈ Architecture

Internet
   |
   v
nginx Reverse Proxy (HTTP on :8000)
   |
   v
Headscale Server (with SQLite)
   |
   +-- Headplane Web GUI (:3001/admin/)

πŸ“ Files Structure

.
β”œβ”€β”€ docker-compose.yml         # Production-ready compose file
β”œβ”€β”€ .env                       # Environment variables
β”œβ”€β”€ nginx.conf                 # Production nginx (SSL/TLS)
β”œβ”€β”€ nginx.dev.conf             # Development nginx (HTTP only)
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ nginx.sh              # nginx management script
β”‚   β”œβ”€β”€ headscale.sh          # Headscale management script
β”‚   β”œβ”€β”€ setup.sh              # Initial setup script
β”‚   └── backup.sh             # Backup script
β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ config.yaml           # Headscale configuration (SQLite)
β”‚   └── policy.json           # ACL policies with tags
β”œβ”€β”€ headplane/
β”‚   └── config.yaml           # Headplane web GUI config
β”œβ”€β”€ docs/                     # Documentation
β”‚   β”œβ”€β”€ QUICK_START_GUI.md   # GUI-only quick start guide
β”‚   β”œβ”€β”€ GUI_SETUP.md         # Complete GUI guide
β”‚   β”œβ”€β”€ BEST_PRACTICES.md    # Production best practices
β”‚   └── NETWORKING.md        # Advanced networking guide
└── data/                     # Headscale data (SQLite DB here)

πŸ”§ Helper Script Usage

The included headscale.sh script simplifies management:

# User management
./scripts/headscale.sh users list
./scripts/headscale.sh users create username

# Pre-auth keys
./scripts/headscale.sh keys create username --reusable --expiration 24h
./scripts/headscale.sh keys list username

# Node management
./scripts/headscale.sh nodes list
./scripts/headscale.sh nodes delete <node-id>

# Routes
./scripts/headscale.sh routes list
./scripts/headscale.sh routes enable <route-id>

# View status
./scripts/headscale.sh status
./scripts/headscale.sh health
./scripts/headscale.sh logs 100

Resources

License

This stack configuration is provided as-is under MIT license.

About

No description, website, or topics provided.

Resources

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors