Making it easy (for me) to deploy production-ready setup
It should be easier to deploy Headscale to Fly.io, without much configuration files.
This repository aims to provide that while still providing a good backup strategy and ensuring your Headscale configuration is persisted, using Litestream
- Fly's account and CLI installed
- An existing S3-compatible bucket (Eg. Digital Ocean, Scaleway, Exoscale)
- Access Key & Secret Key with access to the bucket
- A domain name to use
Give a unique name to your application, something memorable, or use a random
one generated by Fly's automatically (using --generate-name
instead).
Remember to change FLY_APP
with the app name you want to use:
$ flyctl apps create FLY_APP
Now, we need to create a volume in Fly that will be used to store Headscale's configuration and database. This needs to be connected to the application created before:
$ flyctl volumes create --app FLY_APP --region REGION --size 1 FLY_VOLUME
Replace FLY_VOLUME
with the name for the volume to be used by this
application (Eg. hs_data
). Also replace REGION
to one of Fly's regions
closer to your location (see possible values in flyctl platform regions
)
With those in place, we can now set them as environment variables (FLY_APP
and FLY_VOLUME
) and copy our Fly's application template:
$ FLY_APP=my-headscale-app FLY_VOLUME=hs_data envsubst < templates/fly.template.toml > fly.toml
Remember to replace above values with the real names you gave to the both the application and the volume.
This will generate a file named fly.toml
that will be used in future steps.
Before we can setup a domain for our application, we need to allocate an IP address to it.
Let's allocate both IPv4 and IPv6:
$ flyctl ips allocate-v4
$ flyctl ips allocate-v6
Use these IP addresses and update your DNS records to the domain or subdomain
that Headscale will use. For example, we can create A
and AAAA
records for hs.example.com
which will be pointing to this app.
Once DNS has been updated, you can request an SSL certificate be provisioned for the application:
$ flyctl certs add hs.example.com
With the domain name and the S3 configuration, you can copy the template
templates/secrets.template.env
and enter the appropriate values:
$ cp templates/secrets.template.env secrets.env
Make sure you update HEADSCALE_SERVER_URL
and HEADSCALE_BASE_DOMAIN
to
point to the Headscale server URL and the domain you want to use.
Do not forget to include the scheme (https
) and the port (443
) in the
server URL. Example:
HEADSCALE_SERVER_URL=https://hs.example.com:443/
HEADSCALE_BASE_DOMAIN=tn.example.com
Now, you need to enter your S3 credentials, bucket and endpoint, necessary for Litestream to work correctly.
Note that this setup requires an S3 storage and will not work without it. (Also, is not recommended deploy something without a proper backup strategy).
Refer to the documentation of each S3-compatible provider to populate all
the S3_*
variables.
Once done, you can load those into your application:
$ flyctl secrets import < secrets.env
With all the settings in place, we are ready to deploy the application:
$ flyctl deploy
Once app is deployed and green, you can create your first user by using the console:
$ flyctl ssh console
Follow Headscale's own documentation (steps 8 and registration of each machine or preauth keys), example:
$ headscale users create homelab
Now that Headscale is running, to have a 100% reproducible setup, we need to
ensure that private keys initialized by our installation are persisted, so
we need to capture the contents of /data/private.key
and
/data/noise_private.key
and place into secrets of our application.
Within the same Fly console from previous step, obtain the contents of these files:
$ cat /data/private.key
$ cat /data/noise_private.key
Copy those to your clipboard and terminate Fly's console.
Locally, use flyctl
to set HEADSCALE_PRIVATE_KEY
and
HEADSCALE_NOISE_PRIVATE_KEY
with the values obtained before, respectively
$ flyctl secrets set HEADSCALE_PRIVATE_KEY=privkey:abc123... HEADSCALE_NOISE_PRIVATE_KEY=privkey:def456...
Note that applying these two variables will cause your application to restart, but afterwards no other change will be necessary.