Headscale docker-compose config with PostgreSQL
This config is intended for larger installations than our sqlite based standard config. It tends to be slightly easier to back up correctly and will be faster for larger workloads. However, it will consume more RAM especially for low-workload installations and you have two docker containers to worry about during maintenance (though they are managed using a single docker-compose
instance). I do not recommend using a shared postgres server although this is certainly possible.
First, create a random password using
echo POSTGRES_PASSWORD=$(pwgen 30 1) > .env
The docker-compose.yml
looks like this:
services:
headscale:
image: headscale/headscale:latest
volumes:
- ./config:/etc/headscale/
- ./data:/var/lib/headscale
ports:
- 27896:8080
command: serve
restart: unless-stopped
depends_on:
- postgres
postgres:
image: postgres
restart: unless-stopped
volumes:
- ./pg_data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=headscale
- POSTGRES_USER=headscale
Now we create the default headscale config:
mkdir -p ./config
curl https://raw.githubusercontent.com/juanfont/headscale/main/config-example.yaml -o ./config/config.yaml
In config/config.yaml
we need to make these changes:
Set server URL:
server_url: https://headscale.mydomain.com
Comment out the sqlite database (add #
to the front of every line):
# SQLite config
# db_type: sqlite3
# db_path: /var/lib/headscale/db.sqlite
And uncomment and configure postgres:
# Postgres config
db_type: postgres
db_host: postgres
db_port: 5432
db_name: headscale
db_user: headscale
db_pass: ohngooFaciice2hooGoo1Ahvif3ahl
Make sure all of these are uncommented and you copy the password from .env
. It is extremely important that you use a unique password here to prevent attacks from unprivileged host processes to the docker containers.
My recommendation is to reverse proxy headscale using traefik
or nginx
instead of using the builtin Let’s Encrypt / ACME support. This will allow not only sharing the port & IP address with other services, standard services like Traefik and/or nginx are much more well tested regarding exposure to the internet and hence provide a potential security benefit. Additionally, they make it easier to manage certificates in a service-independent manner and provide an additional layer for debugging etc.
You might also configure custom IP address ranges:
ip_prefixes:
- fd5d:7b60:4742::/48
- 100.64.0.0/10
but this is optional.
For more info regarding autostart etc, see How to setup headscale server in 5 minutes using docker-compose