Skip to main content
This guide will help you deploy Headscale locally and connect your first device using command-line tools.

Prerequisites

Before you begin, ensure you have:
  • Docker and Docker Compose installed
  • Basic familiarity with the command line
  • For production: A domain name with DNS configured

Local Development Setup

This setup runs Headscale locally on http://localhost:8000 without SSL/TLS.
1

Start the Stack

Clone the repository and start all services:
docker compose up -d
Check that services are running:
docker compose ps
Verify health endpoint:
curl http://localhost:8000/health
# Should return: {"status":"pass"}
2

Access Headplane Web GUI

First, generate an API key for authentication:
docker exec headscale headscale apikeys create --expiration 999d
Open your browser to:
http://localhost:3001/admin/
Make sure to include the trailing slash in the URL to avoid 404 errors.
Paste the API key when prompted to log in.
3

Create Your First User

Create a user to organize your devices:
docker exec headscale headscale users create myuser
Or use the helper script:
./scripts/headscale.sh users create myuser
List all users to verify:
./scripts/headscale.sh users list
4

Generate a Pre-Auth Key

Create a reusable pre-auth key for connecting devices:
docker exec headscale headscale preauthkeys create --user myuser --reusable --expiration 24h
Or with the helper script:
./scripts/headscale.sh keys create myuser --reusable --expiration 24h
Save this key securely - you’ll need it to connect devices. The key cannot be retrieved later.
5

Connect Your First Device

Install Tailscale on your device, then connect it to your Headscale server.
sudo tailscale up --login-server http://localhost:8000 --authkey YOUR_KEY --accept-routes
Replace YOUR_KEY with the pre-auth key you generated in the previous step.
6

Verify Connection

Check that your device appears in the node list:
./scripts/headscale.sh nodes list
You can also verify in the Headplane web interface at http://localhost:3001/admin/ under the Nodes tab.Test connectivity by pinging another device:
tailscale status
tailscale ping 100.64.0.2

Management Commands

The included helper script simplifies common operations:

User Management

# List all users
./scripts/headscale.sh users list

# Create a new user
./scripts/headscale.sh users create username

# Delete a user
./scripts/headscale.sh users destroy username

Node Management

# List all nodes
./scripts/headscale.sh nodes list

# Delete a node
./scripts/headscale.sh nodes delete <node-id>

# Expire all offline nodes
./scripts/headscale.sh nodes expire

Pre-Auth Keys

# List keys for a user
./scripts/headscale.sh keys list username

# Create a new key
./scripts/headscale.sh keys create username --reusable --expiration 24h

# Create an ephemeral key (node removed when disconnected)
./scripts/headscale.sh keys create username --ephemeral

Routes

# List all routes
./scripts/headscale.sh routes list

# Enable a route
./scripts/headscale.sh routes enable <route-id>

System Status

# Show service status
./scripts/headscale.sh status

# Check health endpoint
./scripts/headscale.sh health

# View logs (default 50 lines)
./scripts/headscale.sh logs

# View more logs
./scripts/headscale.sh logs 100

Production Deployment

For production deployment with SSL/TLS:
1

Configure Environment

cp .env.example .env
nano .env  # Set HEADSCALE_DOMAIN to your domain
Update config/config.yaml:
server_url: https://your-domain.com
Ensure the PostgreSQL password in .env matches the password in config/config.yaml under database.postgres.pass.
2

Obtain SSL Certificates

mkdir -p certbot/conf certbot/www
docker compose up -d nginx

docker compose run --rm certbot certonly \
  --webroot --webroot-path=/var/www/certbot \
  --email [email protected] --agree-tos \
  -d your-domain.com
3

Start Services

docker compose restart nginx
docker compose up -d
Verify:
curl https://your-domain.com/health

Backup and Restore

The stack uses PostgreSQL for data persistence.

Backup Database

# Stop Headscale for consistent backup
docker compose stop headscale

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

# Or backup just the database
docker exec headscale-db pg_dump -U headscale headscale > backup.sql

# Restart Headscale
docker compose start headscale

Restore from Backup

# Stop services
docker compose down

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

# Or restore database
cat backup.sql | docker exec -i headscale-db psql -U headscale

# Restart services
docker compose up -d

Troubleshooting

Check Service Status

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

Headplane 404 Error

Ensure you’re accessing the correct URL with trailing slash:
  • http://localhost:3001/admin/ (correct)
  • http://localhost:3001 (wrong - will return 404)

Connection Issues

# Test Headscale health
curl http://localhost:8000/health

# Check if nodes can reach server
sudo tailscale status
sudo tailscale netcheck

Database Issues

# Verify database connection
docker exec -it headscale-db psql -U headscale -d headscale -c "SELECT 1;"

# Check database size
docker exec headscale-db psql -U headscale -d headscale -c "SELECT pg_size_pretty(pg_database_size('headscale'));"
For detailed configuration options and advanced setups, see the Configuration Guide and Best Practices.

Next Steps

Build docs developers (and LLMs) love