> ## Content Index
> Fetch the complete content index at: https://blog.alphavps.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Hosting Headscale In a Container With Docker
- URL: https://blog.alphavps.com/hosting-headscale-in-a-container-with-docker/
- Published: 2026-02-16T08:37:07.000Z
- Updated: 2026-02-16T08:37:07.000Z
- Author: Asen Velchov
- Tags: Tutorials, Docker & Containers, Self-Hosting, VPN & Remote Access, #series-headscale

## What is Tailscale

[](https://github.com/juanfont/headscale?ref=blog.alphavps.com#what-is-tailscale)

Tailscale is a WireGuard-based modern VPN. Basically, it works as an overlay network in the computers between your networks — uses NAT traversal.

Its role is to be an exchange point of WireGuard public keys among Tailscale network nodes. It gives clients their IP addresses, sets the demarcation line between each user, allows users to share machines, and exposes your nodes' routes.

Based on private users or an organization, Tailscale assigns a user a private network (tailnet).

### What is headscale?

Headscale is an open-source, self-hosted implementation of the Tailscale control server.

Headscale aims to implement a self-hosted, open-source alternative to the Tailscale control server. Headscale aims 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 personal use or a small open-source organization.

In this guide, we will be showing how to set up and run Headscale in a container.

### Prerequisites

- [Docker installed](https://blog.alphavps.com/intro-to-docker/)
- [VPS](https://alphavps.com/?ref=blog.alphavps.com)
- FQDN (although only an IP can also be used)
- (Optional) Subdomain pointing to the IP of your VPS

## Configure Headscale

First, make a directory on the host Docker node used to hold Headscale configuration and the SQLite database:

- `mkdir -p /root/.headscale/{config,lib,run}`
- `cd /root/.headscale/config`

Then, [Download](https://github.com/juanfont/headscale/blob/main/config-example.yaml?ref=blog.alphavps.com) the example configuration for your chosen version and save it as: `/root/.headscale/config/config.yaml`

The lines that will definitely need modifying are:

  
`server_url`\- Here, you will need to set the domain with a subdomain preceding it, like `headscale.example.com` or IP. Append the port on which you will be running the service.   
  
`listen_addr`\- For production, you will need to set this to your public IP, followed by the port.  
  
`metrics_listen_addr`\- This is best kept on your local/private network. The default is already configured as such.  
  
`tls_letsencrypt_hostname`\- This must be set to your server URL for the automatic TLS certificate. Omit if you are using only IP.  
  
`base_domain`\- Defines the base domain to create the hostnames for MagicDNS. So if your URL is headscale.example.com, you will need to enter `example.com`  

## Run Headscale

After the configuration is complete, start the Headscale server.

```
docker run \
  --name headscale \
  --net=host \
  --detach \
  --volume "/root/.headscale/config:/etc/headscale" \
  --volume "/root/.headscale/lib:/var/lib/headscale" \
  --volume "/root/.headscale/run:/var/run/headscale" \
  --no-healthcheck \
  docker.io/headscale/headscale:<VERSION> \
  serve
```

Replace <VERSION> with the version of headscale you would like to run.

ℹ️

As you may have noticed, we have stopped the health checks for this container. As of writing of this guide i.e. 15.11.2025, the headscale container doesn't have any kind of shell. The `--health-cmd` flag used to check health of containers requires the existence of `/bin/sh` , thus the health check automatically fails and the container is marked as unhealthy. Possible solutions for this would be to run a healthcheck from the host OS with the command `docker exec -it headscale headscale health` or making a custom container with `sh` included.

  
And finally, verify that Headscale is running:

Follow the container logs:

- `docker logs --follow headscale`

If no errors occurred, you might get an output similar to this:

![](https://blog.alphavps.com/content/images/2025/11/image-2.png)

Verify running containers:

- `docker ps`

Verify Headscale is available:

- `curl http://127.0.0.1:9090/metrics` \- If Headscale is running correctly, by running this command, you will get a lot of metrics regarding the container.

##   
Post-Install Setup 

We will use Tailnet as a client for our Headscale server.  
We will create two users to test out connectivity between them.

```
docker exec -it headscale headscale users create user1

docker exec -it headscale headscale users create user2

```

We can take a look at the created users with the command:

```
docker exec -it headscale headscale users list
```

![](https://blog.alphavps.com/content/images/2025/11/image-3.png)

###   
Register a machine (normal login)

To register a machine when running Headscale in a container, take the Headscale command and pass it to the container:

```
docker exec -it headscale \
  headscale nodes register --user <USER_ID> --key <YOUR_MACHINE_KEY>
```

### Register a machine using a pre-authenticated key

Generate a key using the command line:

```
docker exec -it headscale \
  headscale preauthkeys create --user <USER_ID> --reusable --expiration 24h
```

ℹ️

Replace <USER\_ID> with the actual ID of the user, which can be seen via the output of the `users list` command as shown above

  
This will return a pre-authenticated key that can be used to connect a node to Headscale 

### Downloading the client and logging in

Go to [tailscale.com](https://tailscale.com/?ref=blog.alphavps.com) and download the client for your OS, then open a terminal (or PowerShell if you are running Windows) and run the following commands:

### Authenticating using a pre-authenticated key:

- `tailscale up --login-server <YOUR_HEADSCALE_URL> --authkey <YOUR_AUTH_KEY>`

###  Authenticating using a normal login

- `tailscale up --login-server YOUR_HEADSCALE_URL`

We can run the command

```
docker exec -it headscale headscale nodes list
```

To check the nodes that are currently connected and their IPs. I have connected two nodes to Headscale.  

![](https://blog.alphavps.com/content/images/2025/11/image-4.png)

We can now run a simple ping test to determine if the setup is working.   

![](https://blog.alphavps.com/content/images/2025/11/image-5.png)

First VM pinging second VM

![](https://blog.alphavps.com/content/images/2025/11/image-6.png)

Second VM pinging first VM

##   
Debugging headscale running in Docker

The `headscale/headscale` Docker container is based on a "distroless" image that does not contain a shell or any other debug tools. If you need to debug your application running in a Docker container, you can use the `-debug` variant, for example `headscale/headscale:x.x.x-debug`.

### Running the debug Docker container

To run the debug Docker container, use the exact same commands as above, but replace `headscale/headscale:x.x.x` with `headscale/headscale:x.x.x-debug` (`x.x.x` is the version of Headscale). The two containers are compatible with each other, so you can alternate between them.

### Executing commands in the debug container

The default command in the debug container is to run `headscale`, which is located at `/ko-app/headscale` inside the container.

Additionally, the debug container includes a minimalist Busybox shell.

To launch a shell in the container, use:

`docker run -it headscale/headscale:x.x.x-debug sh`

You can also execute commands directly, such as `ls /ko-app` In this example:

`docker run headscale/headscale:x.x.x-debug ls /ko-app`

Using `docker exec -it` allows you to run commands in an existing container.  

## Conclusion  

In this guide, we successfully installed and configured a self-hosted Headcale Docker container. We also learned how to create users and connect to our Headscale node using Tailscale. Along the way, we touched on some intricacies in using a containerized Headscale. Now you are ready to establish WireGuard-based connections between your services!