We aim to support the last 10 releases of the Tailscale client on all provided operating systems and platforms. Some platforms might require additional configuration to connect with headscale.
OS
Supports headscale
Linux
Yes
OpenBSD
Yes
FreeBSD
Yes
Windows
Yes (see docs and /windows on your headscale for more information)
We aim to support the last 10 releases of the Tailscale client on all provided operating systems and platforms. Some platforms might require additional configuration to connect with headscale.
OS
Supports headscale
Linux
Yes
OpenBSD
Yes
FreeBSD
Yes
Windows
Yes (see docs and /windows on your headscale for more information)
Headscale is "Open Source, acknowledged contribution", this means that any contribution will have to be discussed with the maintainers before being added to the project. This model has been chosen to reduce the risk of burnout by limiting the maintenance overhead of reviewing and validating third-party code.
Headscale has a small maintainer team that tries to balance working on the project, fixing bugs and reviewing contributions.
When we work on issues ourselves, we develop first hand knowledge of the code and it makes it possible for us to maintain and own the code as the project develops.
Code contributions are seen as a positive thing. People enjoy and engage with our project, but it also comes with some challenges; we have to understand the code, we have to understand the feature, we might have to become familiar with external libraries or services and we think about security implications. All those steps are required during the reviewing process. After the code has been merged, the feature has to be maintained. Any changes reliant on external services must be updated and expanded accordingly.
The review and day-1 maintenance adds a significant burden on the maintainers. Often we hope that the contributor will help out, but we found that most of the time, they disappear after their new feature was added.
This means that when someone contributes, we are mostly happy about it, but we do have to run it through a series of checks to establish if we actually can maintain this feature.
A general description is provided here and an explicit list is provided in our pull request template.
All new features have to start out with a design document, which should be discussed on the issue tracker (not discord). It should include a use case for the feature, how it can be implemented, who will implement it and a plan for maintaining it.
All features have to be end-to-end tested (integration tests) and have good unit test coverage to ensure that they work as expected. This will also ensure that the feature continues to work as expected over time. If a change cannot be tested, a strong case for why this is not possible needs to be presented.
The contributor should help to maintain the feature over time. In case the feature is not maintained probably, the maintainers reserve themselves the right to remove features they redeem as unmaintainable. This should help to improve the quality of the software and keep it in a maintainable state.
Headscale is "Open Source, acknowledged contribution", this means that any contribution will have to be discussed with the maintainers before being added to the project. This model has been chosen to reduce the risk of burnout by limiting the maintenance overhead of reviewing and validating third-party code.
Headscale has a small maintainer team that tries to balance working on the project, fixing bugs and reviewing contributions.
When we work on issues ourselves, we develop first hand knowledge of the code and it makes it possible for us to maintain and own the code as the project develops.
Code contributions are seen as a positive thing. People enjoy and engage with our project, but it also comes with some challenges; we have to understand the code, we have to understand the feature, we might have to become familiar with external libraries or services and we think about security implications. All those steps are required during the reviewing process. After the code has been merged, the feature has to be maintained. Any changes reliant on external services must be updated and expanded accordingly.
The review and day-1 maintenance adds a significant burden on the maintainers. Often we hope that the contributor will help out, but we found that most of the time, they disappear after their new feature was added.
This means that when someone contributes, we are mostly happy about it, but we do have to run it through a series of checks to establish if we actually can maintain this feature.
A general description is provided here and an explicit list is provided in our pull request template.
All new features have to start out with a design document, which should be discussed on the issue tracker (not discord). It should include a use case for the feature, how it can be implemented, who will implement it and a plan for maintaining it.
All features have to be end-to-end tested (integration tests) and have good unit test coverage to ensure that they work as expected. This will also ensure that the feature continues to work as expected over time. If a change cannot be tested, a strong case for why this is not possible needs to be presented.
The contributor should help to maintain the feature over time. In case the feature is not maintained probably, the maintainers reserve themselves the right to remove features they redeem as unmaintainable. This should help to improve the quality of the software and keep it in a maintainable state.
Headscale aims to implement a self-hosted, open source alternative to the Tailscale control server. Headscale's goal is to provide self-hosters and hobbyists with an open-source server they can use for their projects and labs. It implements a narrow scope, a single Tailnet, suitable for a personal use, or a small open-source organisation.
Headscale is "Open Source, acknowledged contribution", this means that any contribution will have to be discussed with the Maintainers before being submitted.
Why is 'acknowledged contribution' the chosen model?¶
Both maintainers have full-time jobs and families, and we want to avoid burnout. We also want to avoid frustration from contributors when their PRs are not accepted.
We are more than happy to exchange emails, or to have dedicated calls before a PR is submitted.
For convenience, we also build Docker images with headscale. But please be aware that we don't officially support deploying headscale using Docker. On our Discord server we have a "docker-issues" channel where you can ask for Docker-specific help to the community.
We recommend the use of SQLite as database for headscale:
SQLite is simple to setup and easy to use
It scales well for all of headscale's usecases
Development and testing happens primarily on SQLite
PostgreSQL is still supported, but is considered to be in "maintenance mode"
The headscale project itself does not provide a tool to migrate from PostgreSQL to SQLite. Please have a look at the related tools documentation for migration tooling provided by the community.
Why is my reverse proxy not working with headscale?¶
We don't know. We don't use reverse proxies with headscale ourselves, so we don't have any experience with them. We have community documentation on how to configure various reverse proxies, and a dedicated "reverse-proxy-issues" channel on our Discord server where you can ask for help to the community.
Can I use headscale and tailscale on the same machine?¶
Running headscale on a machine that is also in the tailnet can cause problems with subnet routers, traffic relay nodes, and MagicDNS. It might work, but it is not supported.
Headscale aims to implement a self-hosted, open source alternative to the Tailscale control server. Headscale's goal is to provide self-hosters and hobbyists with an open-source server they can use for their projects and labs. It implements a narrow scope, a single Tailnet, suitable for a personal use, or a small open-source organisation.
Headscale is "Open Source, acknowledged contribution", this means that any contribution will have to be discussed with the Maintainers before being submitted.
Why is 'acknowledged contribution' the chosen model?¶
Both maintainers have full-time jobs and families, and we want to avoid burnout. We also want to avoid frustration from contributors when their PRs are not accepted.
We are more than happy to exchange emails, or to have dedicated calls before a PR is submitted.
For convenience, we also build Docker images with headscale. But please be aware that we don't officially support deploying headscale using Docker. On our Discord server we have a "docker-issues" channel where you can ask for Docker-specific help to the community.
We recommend the use of SQLite as database for headscale:
SQLite is simple to setup and easy to use
It scales well for all of headscale's usecases
Development and testing happens primarily on SQLite
PostgreSQL is still supported, but is considered to be in "maintenance mode"
The headscale project itself does not provide a tool to migrate from PostgreSQL to SQLite. Please have a look at the related tools documentation for migration tooling provided by the community.
Why is my reverse proxy not working with headscale?¶
We don't know. We don't use reverse proxies with headscale ourselves, so we don't have any experience with them. We have community documentation on how to configure various reverse proxies, and a dedicated "reverse-proxy-issues" channel on our Discord server where you can ask for help to the community.
Can I use headscale and tailscale on the same machine?¶
Running headscale on a machine that is also in the tailnet can cause problems with subnet routers, traffic relay nodes, and MagicDNS. It might work, but it is not supported.
\ No newline at end of file
diff --git a/development/about/features/index.html b/development/about/features/index.html
index 07d17939..f85fb978 100644
--- a/development/about/features/index.html
+++ b/development/about/features/index.html
@@ -1 +1 @@
- Features - Headscale
Headscale aims to implement a self-hosted, open source alternative to the Tailscale control server. Headscale's goal is to provide self-hosters and hobbyists with an open-source server they can use for their projects and labs. This page provides on overview of headscale's feature and compatibility with the Tailscale control server:
Headscale aims to implement a self-hosted, open source alternative to the Tailscale control server. Headscale's goal is to provide self-hosters and hobbyists with an open-source server they can use for their projects and labs. This page provides on overview of headscale's feature and compatibility with the Tailscale control server:
\ No newline at end of file
diff --git a/development/about/help/index.html b/development/about/help/index.html
index 79dba586..40a24e42 100644
--- a/development/about/help/index.html
+++ b/development/about/help/index.html
@@ -1 +1 @@
- Getting help - Headscale
All headscale releases are available on the GitHub release page. Those releases are available as binaries for various platforms and architectures, packages for Debian based systems and source code archives. Container images are available on Docker Hub.
An Atom/RSS feed of headscale releases is available here.
See the "announcements" channel on our Discord server for news about headscale.
\ No newline at end of file
+ Releases - Headscale
All headscale releases are available on the GitHub release page. Those releases are available as binaries for various platforms and architectures, packages for Debian based systems and source code archives. Container images are available on Docker Hub.
An Atom/RSS feed of headscale releases is available here.
See the "announcements" channel on our Discord server for news about headscale.
\ No newline at end of file
diff --git a/development/about/sponsor/index.html b/development/about/sponsor/index.html
index 4070d2a9..a0063efe 100644
--- a/development/about/sponsor/index.html
+++ b/development/about/sponsor/index.html
@@ -1 +1 @@
- Sponsor - Headscale
If you like to support the development of headscale, please consider a donation via ko-fi.com/headscale. Thank you!
\ No newline at end of file
diff --git a/development/index.html b/development/index.html
index 549ed0ad..9e07412f 100644
--- a/development/index.html
+++ b/development/index.html
@@ -1 +1 @@
- Headscale
Headscale aims to implement a self-hosted, open source alternative to the Tailscale control server. Headscale's goal is to provide self-hosters and hobbyists with an open-source server they can use for their projects and labs. It implements a narrower scope, a single Tailnet, suitable for a personal use, or a small open-source organisation.
Headscale is "Open Source, acknowledged contribution", this means that any contribution will have to be discussed with the Maintainers before being submitted.
Headscale aims to implement a self-hosted, open source alternative to the Tailscale control server. Headscale's goal is to provide self-hosters and hobbyists with an open-source server they can use for their projects and labs. It implements a narrower scope, a single Tailnet, suitable for a personal use, or a small open-source organisation.
Headscale is "Open Source, acknowledged contribution", this means that any contribution will have to be discussed with the Maintainers before being submitted.
When using ACL's the User borders are no longer applied. All machines whichever the User have the ability to communicate with other hosts as long as the ACL's permits this exchange.
Let's build an example use case for a small business (It may be the place where ACL's are the most useful).
We have a small company with a boss, an admin, two developers and an intern.
The boss should have access to all servers but not to the user's hosts. Admin should also have access to all hosts except that their permissions should be limited to maintaining the hosts (for example purposes). The developers can do anything they want on dev hosts but only watch on productions hosts. Intern can only interact with the development servers.
There's an additional server that acts as a router, connecting the VPN users to an internal network 10.20.0.0/16. Developers must have access to those internal resources.
Each user have at least a device connected to the network and we have some servers.
When registering the servers we will need to add the flag --advertise-tags=tag:<tag1>,tag:<tag2>, and the user that is registering the server should be allowed to do it. Since anyone can add tags to a server they can register, the check of the tags is done on headscale server and only valid tags are applied. A tag is valid if the user that is registering it is allowed to do it.
To use ACLs in headscale, you must edit your config.yaml file. In there you will find a policy.path parameter. This will need to point to your ACL file. More info on how these policies are written can be found here.
Here are the ACL's to implement the same permissions as above:
When using ACL's the User borders are no longer applied. All machines whichever the User have the ability to communicate with other hosts as long as the ACL's permits this exchange.
Let's build an example use case for a small business (It may be the place where ACL's are the most useful).
We have a small company with a boss, an admin, two developers and an intern.
The boss should have access to all servers but not to the user's hosts. Admin should also have access to all hosts except that their permissions should be limited to maintaining the hosts (for example purposes). The developers can do anything they want on dev hosts but only watch on productions hosts. Intern can only interact with the development servers.
There's an additional server that acts as a router, connecting the VPN users to an internal network 10.20.0.0/16. Developers must have access to those internal resources.
Each user have at least a device connected to the network and we have some servers.
When registering the servers we will need to add the flag --advertise-tags=tag:<tag1>,tag:<tag2>, and the user that is registering the server should be allowed to do it. Since anyone can add tags to a server they can register, the check of the tags is done on headscale server and only valid tags are applied. A tag is valid if the user that is registering it is allowed to do it.
To use ACLs in headscale, you must edit your config.yaml file. In there you will find a policy.path parameter. This will need to point to your ACL file. More info on how these policies are written can be found here.
Here are the ACL's to implement the same permissions as above:
{// groups are collections of users having a common scope. A user can be in multiple groups// groups cannot be composed of groups"groups":{
diff --git a/development/ref/configuration/index.html b/development/ref/configuration/index.html
index 9192f207..334246c2 100644
--- a/development/ref/configuration/index.html
+++ b/development/ref/configuration/index.html
@@ -1,4 +1,4 @@
- Configuration - Headscale
Always select the same GitHub tag as the released version you use to ensure you have the correct example configuration. The main branch might contain unreleased changes.
Always select the same GitHub tag as the released version you use to ensure you have the correct example configuration. The main branch might contain unreleased changes.
This page is not actively maintained by the headscale authors and is written by community members. It is not verified by headscale developers.
It might be outdated and it might miss necessary steps.
Headscale allows to set custom DNS records which are made available via MagicDNS. An example use case is to serve multiple apps on the same host via a reverse proxy like NGINX, in this case a Prometheus monitoring stack. This allows to nicely access the service with "http://grafana.myvpn.example.com" instead of the hostname and port combination "http://hostname-in-magic-dns.myvpn.example.com:3000".
This page is not actively maintained by the headscale authors and is written by community members. It is not verified by headscale developers.
It might be outdated and it might miss necessary steps.
Headscale allows to set custom DNS records which are made available via MagicDNS. An example use case is to serve multiple apps on the same host via a reverse proxy like NGINX, in this case a Prometheus monitoring stack. This allows to nicely access the service with "http://grafana.myvpn.example.com" instead of the hostname and port combination "http://hostname-in-magic-dns.myvpn.example.com:3000".
This page is not actively maintained by the headscale authors and is written by community members. It is not verified by headscale developers.
It might be outdated and it might miss necessary steps.
Running headscale behind a reverse proxy is useful when running multiple applications on the same server, and you want to reuse the same external IP and port - usually tcp/443 for HTTPS.
The reverse proxy MUST be configured to support WebSockets to communicate with Tailscale clients.
WebSockets support is also required when using the headscale embedded DERP server. In this case, you will also need to expose the UDP port used for STUN (by default, udp/3478). Please check our config-example.yaml.
Running headscale behind a cloudflare proxy or cloudflare tunnel is not supported and will not work as Cloudflare does not support WebSocket POSTs as required by the Tailscale protocol. See this issue
Headscale can be configured not to use TLS, leaving it to the reverse proxy to handle. Add the following configuration values to your headscale config file.
server_url:https://<YOUR_SERVER_NAME># This should be the FQDN at which headscale will be served
+ Reverse proxy - Headscale
This page is not actively maintained by the headscale authors and is written by community members. It is not verified by headscale developers.
It might be outdated and it might miss necessary steps.
Running headscale behind a reverse proxy is useful when running multiple applications on the same server, and you want to reuse the same external IP and port - usually tcp/443 for HTTPS.
The reverse proxy MUST be configured to support WebSockets to communicate with Tailscale clients.
WebSockets support is also required when using the headscale embedded DERP server. In this case, you will also need to expose the UDP port used for STUN (by default, udp/3478). Please check our config-example.yaml.
Running headscale behind a cloudflare proxy or cloudflare tunnel is not supported and will not work as Cloudflare does not support WebSocket POSTs as required by the Tailscale protocol. See this issue
Headscale can be configured not to use TLS, leaving it to the reverse proxy to handle. Add the following configuration values to your headscale config file.
server_url:https://<YOUR_SERVER_NAME># This should be the FQDN at which headscale will be servedlisten_addr:0.0.0.0:8080metrics_listen_addr:0.0.0.0:9090tls_cert_path:""
diff --git a/development/ref/integration/tools/index.html b/development/ref/integration/tools/index.html
index b323c8b9..f927b0d0 100644
--- a/development/ref/integration/tools/index.html
+++ b/development/ref/integration/tools/index.html
@@ -1 +1 @@
- Tools - Headscale
\ No newline at end of file
diff --git a/development/ref/integration/web-ui/index.html b/development/ref/integration/web-ui/index.html
index 15f9012b..e21cf10a 100644
--- a/development/ref/integration/web-ui/index.html
+++ b/development/ref/integration/web-ui/index.html
@@ -1 +1 @@
- Web UI - Headscale
In your config.yaml, customize this to your liking:
oidc:# Block further startup until the OIDC provider is healthy and availableonly_start_if_oidc_is_available:true# Specified by your OIDC provider
diff --git a/development/ref/remote-cli/index.html b/development/ref/remote-cli/index.html
index aabc5fb2..8b36d480 100644
--- a/development/ref/remote-cli/index.html
+++ b/development/ref/remote-cli/index.html
@@ -1,4 +1,4 @@
- Remote CLI - Headscale
We need to create an API key to authenticate with the remote headscale server when using it from our workstation.
To create an API key, log into your headscale server and generate a key:
headscaleapikeyscreate--expiration90d
Copy the output of the command and save it for later. Please note that you can not retrieve a key again, if the key is lost, expire the old one, and create a new key.
To list the keys currently associated with the server:
Headscale can be configured to expose its web service via TLS. To configure the certificate and key file manually, set the tls_cert_path and tls_cert_path configuration parameters. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from.
Headscale can be configured to expose its web service via TLS. To configure the certificate and key file manually, set the tls_cert_path and tls_cert_path configuration parameters. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from.
tls_cert_path:""tls_key_path:""
The certificate should contain the full chain, else some clients, like the Tailscale Android client, will reject it.
To get a certificate automatically via Let's Encrypt, set tls_letsencrypt_hostname to the desired certificate hostname. This name must resolve to the IP address(es) headscale is reachable on (i.e., it must correspond to the server_url configuration parameter). The certificate and Let's Encrypt account credentials will be stored in the directory configured in tls_letsencrypt_cache_dir. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from.
Click "Deploy Application" on the template page to start deployment. Upon completion, two applications appear: headscale, and one of its web interfaces.
Once deployment concludes, click 'Details' on the headscale application page to navigate to the application's details.
Wait for the application's status to switch to running. For accessing the headscale server, the Public Address associated with port 8080 is the address of the headscale server. To access the headscale console, simply append /admin/ to the headscale public URL.
Click "Deploy Application" on the template page to start deployment. Upon completion, two applications appear: headscale, and one of its web interfaces.
Once deployment concludes, click 'Details' on the headscale application page to navigate to the application's details.
Wait for the application's status to switch to running. For accessing the headscale server, the Public Address associated with port 8080 is the address of the headscale server. To access the headscale console, simply append /admin/ to the headscale public URL.
\ No newline at end of file
diff --git a/development/setup/install/community/index.html b/development/setup/install/community/index.html
index 5299fa38..336d5106 100644
--- a/development/setup/install/community/index.html
+++ b/development/setup/install/community/index.html
@@ -1,4 +1,4 @@
- Community packages - Headscale
Several Linux distributions and community members provide packages for headscale. Those packages may be used instead of the official releases provided by the headscale maintainers. Such packages offer improved integration for their targeted operating system and usually:
setup a dedicated user account to run headscale
provide a default configuration
install headscale as system service
Community packages might be outdated
The packages mentioned on this page might be outdated or unmaintained. Use the official releases to get the current stable version or to test pre-releases.
Several Linux distributions and community members provide packages for headscale. Those packages may be used instead of the official releases provided by the headscale maintainers. Such packages offer improved integration for their targeted operating system and usually:
setup a dedicated user account to run headscale
provide a default configuration
install headscale as system service
Community packages might be outdated
The packages mentioned on this page might be outdated or unmaintained. Use the official releases to get the current stable version or to test pre-releases.
This page is not actively maintained by the headscale authors and is written by community members. It is not verified by headscale developers.
It might be outdated and it might miss necessary steps.
This documentation has the goal of showing a user how-to set up and run headscale in a container. Docker is used as the reference container implementation, but there is no reason that it should not work with alternatives like Podman. The Docker image can be found on Docker Hub here.
This page is not actively maintained by the headscale authors and is written by community members. It is not verified by headscale developers.
It might be outdated and it might miss necessary steps.
This documentation has the goal of showing a user how-to set up and run headscale in a container. Docker is used as the reference container implementation, but there is no reason that it should not work with alternatives like Podman. The Docker image can be found on Docker Hub here.
Prepare a directory on the host Docker node in your directory of choice, used to hold headscale configuration and the SQLite database:
mkdir-p./headscale/config
cd./headscale
Download the example configuration for your chosen version and save it as: /etc/headscale/config.yaml. Adjust the configuration to suit your local environment. See Configuration for details.
Official releases for headscale are available as binaries for various platforms and DEB packages for Debian and Ubuntu. Both are available on the GitHub releases page.
It is recommended to use our DEB packages to install headscale on a Debian based system as those packages configure a user to run headscale, provide a default configuration and ship with a systemd service file. Supported distributions are Ubuntu 20.04 or newer, Debian 11 or newer.
Official releases for headscale are available as binaries for various platforms and DEB packages for Debian and Ubuntu. Both are available on the GitHub releases page.
It is recommended to use our DEB packages to install headscale on a Debian based system as those packages configure a user to run headscale, provide a default configuration and ship with a systemd service file. Supported distributions are Ubuntu 20.04 or newer, Debian 11 or newer.
HEADSCALE_VERSION=""# See above URL for latest version, e.g. "X.Y.Z" (NOTE: do not add the "v" prefix!)HEADSCALE_ARCH=""# Your system architecture, e.g. "amd64"wget--output-document=headscale.deb\"https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
diff --git a/development/setup/install/source/index.html b/development/setup/install/source/index.html
index 0e9084f0..ee2db787 100644
--- a/development/setup/install/source/index.html
+++ b/development/setup/install/source/index.html
@@ -1,4 +1,4 @@
- Build from source - Headscale
The headscale documentation and the provided examples are written with a few assumptions in mind:
Headscale is running as system service via a dedicated user headscale.
The configuration is loaded from /etc/headscale/config.yaml.
SQLite is used as database.
The data directory for headscale (used for private keys, ACLs, SQLite database, …) is located in /var/lib/headscale.
URLs and values that need to be replaced by the user are either denoted as <VALUE_TO_CHANGE> or use placeholder values such as headscale.example.com.
Please adjust to your local environment accordingly.
The Tailscale client assumes HTTPS on port 443 in certain situations. Serving headscale either via HTTP or via HTTPS on a port other than 443 is possible but sticking with HTTPS on port 443 is strongly recommended for production setups. See issue 2164 for more information. ↩
\ No newline at end of file
+ Requirements and Assumptions - Headscale
The headscale documentation and the provided examples are written with a few assumptions in mind:
Headscale is running as system service via a dedicated user headscale.
The configuration is loaded from /etc/headscale/config.yaml.
SQLite is used as database.
The data directory for headscale (used for private keys, ACLs, SQLite database, …) is located in /var/lib/headscale.
URLs and values that need to be replaced by the user are either denoted as <VALUE_TO_CHANGE> or use placeholder values such as headscale.example.com.
Please adjust to your local environment accordingly.
The Tailscale client assumes HTTPS on port 443 in certain situations. Serving headscale either via HTTP or via HTTPS on a port other than 443 is possible but sticking with HTTPS on port 443 is strongly recommended for production setups. See issue 2164 for more information. ↩
\ No newline at end of file
diff --git a/development/setup/upgrade/index.html b/development/setup/upgrade/index.html
index 7e75f716..8d302cb5 100644
--- a/development/setup/upgrade/index.html
+++ b/development/setup/upgrade/index.html
@@ -1 +1 @@
- Upgrade - Headscale
Open the app and select the settings menu in the upper-right corner
Tap on Accounts
In the kebab menu icon (three dots) in the upper-right corner select Use an alternate server
Enter your server URL (e.g https://headscale.example.com) and follow the instructions
\ No newline at end of file
diff --git a/development/usage/connect/apple/index.html b/development/usage/connect/apple/index.html
index 8eae9460..ffdc2235 100644
--- a/development/usage/connect/apple/index.html
+++ b/development/usage/connect/apple/index.html
@@ -1,2 +1,2 @@
- Apple - Headscale
Open Tailscale and make sure you are not logged in to any account
Open Settings on the iOS device
Scroll down to the third party apps section, under Game Center or TV Provider
Find Tailscale and select it
If the iOS device was previously logged into Tailscale, switch the Reset Keychain toggle to on
Enter the URL of your headscale instance (e.g https://headscale.example.com) under Alternate Coordination Server URL
Restart the app by closing it from the iOS app switcher, open the app and select the regular sign in option (non-SSO). It should open up to the headscale authentication page.
Enter your credentials and log in. Headscale should now be working on your iOS device.