Self-hosted service
kaaanata/derper-docker avatar
kaaanata/derper-docker

derper-docker packages a Tailscale relay server in one container

tailscale‘s selfhosted derp-server docker image

521 stars121 forksDockerfileGPL-3.0

At a glance

What is it?
kaaanata/derper-docker is a GPL-3.0 Docker image that builds the Tailscale derper relay binary from source and ships it on Ubuntu with every setting exposed as an environment variable, from the hostname and certificate mode to the STUN port and an optional client verification hook. It is a small piece of infrastructure, and the environment table is the whole specification.
Who is it for?
derper-docker is a good fit for someone who wants to run their own Tailscale relay instead of relying on the public one, and who is comfortable with a container that needs a hostname, a certificate and two or three published ports. It is a poor fit for a team that has no reason to operate a relay, and a poor fit if you need the upstream official documentation to match a newer derper, since the image pins its own build.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 46 days ago.
What is it written in?
Mainly Dockerfile, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What a DERP server actually is

A DERP server is the relay that carries encrypted traffic between Tailscale peers that cannot reach each other directly. Tailscale runs public ones, and the official documentation on custom DERP servers is the reference this project points at. The reason to run your own is reachability and control: a relay you operate sits inside your network, and the traffic that goes through it is yours to reason about. The scale of the thing is worth being clear about before you install it, because the image is small and the operational surface is small too. The image is published as ghcr.io/kaaanata/derper for both amd64 and arm64, and the README links the Docker Hub pull count badge and the build workflow badge from the upstream derper-docker project by fredliang44, which suggests this is a maintained derivative rather than a one-off personal build.

The one command that starts it

Setup needs one environment variable, DERP_DOMAIN, set to your own domain, and the run command publishes the three ports the relay needs:

bash
docker run -e DERP_DOMAIN=derper.your-domain.com -p 80:80 -p 443:443 -p 3478:3478/udp ghcr.io/kaaanata/derper

Port 80 is there so the certificate can be issued, port 443 is the relay itself over TLS, and 3478 over UDP is the STUN service. The domain has to resolve to the host before the container can do anything useful, since the certificate is issued for that name, which is why the README puts the requirement at the top of the setup section as a callout rather than burying it. The image ships a default of your-hostname.com for the variable, so a forgotten DERP_DOMAIN produces a container that starts and then fails to get a usable certificate rather than one that refuses to start.

The environment table is the whole configuration

Everything is an environment variable, and the README tabulates them with whether they are required, what they mean and their default. DERP_DOMAIN is the only required one, described as the derper server hostname. DERP_CERT_DIR is where the Let's Encrypt certificates are stored when the address port is 443, defaulting to /app/certs. DERP_CERT_MODE selects how the certificate is obtained, with the options manual and letsencrypt, defaulting to letsencrypt. DERP_ADDR is the listening server address, defaulting to :443. DERP_STUN turns on the STUN server as well and defaults to true, with DERP_STUN_PORT setting the UDP port for it at 3478, and DERP_HTTP_PORT setting the HTTP port at 80, where a value of -1 disables it. The last two are the security-relevant pair: DERP_VERIFY_CLIENTS verifies clients to this DERP server through a local tailscaled instance and defaults to false, and DERP_VERIFY_CLIENT_URL is an admission controller URL for permitting client connections.

Client verification, and why it needs a socket

The verification pair is the part that needs thought before you switch it on. DERP_VERIFY_CLIENTS makes the server check that a connecting client is one Tailscale knows about, and it does so by consulting a local tailscaled instance. The README is explicit that the container needs access to Tailscale's Local API for this, which can usually be reached through /var/run/tailscale/tailscaled.sock, and it says that if you are running Tailscale bare metal on Linux, adding a volume mount to the docker run command should be enough, mounting /var/run/tailscale/tailscaled.sock into the container at the same path.

That is a Unix domain socket belonging to the host's tailscaled being handed to the container, which is a real trust boundary: a process in the container can then talk to the local Tailscale API. On a host that is not running Tailscale bare metal, the same mount does not apply and you need another route to that API. The other option, DERP_VERIFY_CLIENT_URL, points at an admission controller instead, which is the cleaner arrangement if you already run one.

How the image is built

The Dockerfile is two stages and shows exactly what ends up in the final image. The builder stage is golang:latest with a working directory of /app, and it takes a build argument DERP_VERSION defaulting to latest, installing the binary with go install tailscale.com/cmd/derper at that version. The runtime stage is ubuntu, with DEBIAN_FRONTEND set to noninteractive, and it installs apt-utils and ca-certificates and creates /app/certs, which is the default certificate directory. The final image then declares each setting as an environment variable with the same default the README tabulates, and copies the built binary across from the builder with COPY --from=builder /go/bin/derper. The command at the end passes the variables through as flags: hostname, certmode, certdir, the address, the STUN settings, the HTTP port and the two verification options. There is no entrypoint script and no shell wrapper, so the container's process is derper with its arguments and nothing else.

Where this image is the wrong choice

Three cases. The first is a team with no reason to run a relay, because the public DERP servers exist and running your own means a hostname, a certificate renewal, a public address and someone on call when it breaks. The second is an environment where the host is not Tailscale bare metal and you want client verification, because the documented route to the Local API assumes exactly that setup, and the alternative admission controller URL is infrastructure you may not have. The third is a deployment that needs a specific derper build, since the image resolves the version through the DERP_VERSION build argument at image build time, and the default of latest means two people building the image on different days can get different binaries. Against building derper from source yourself, the difference in approach is that this image is a packaging decision, not a code change: the binary is upstream, the Dockerfile adds the environment variable surface and the certificate directory, and the value is convenience rather than behaviour.

Editorial conclusion

derper-docker is a good fit for someone who wants to run their own Tailscale relay instead of relying on the public one, and who is comfortable with a container that needs a hostname, a certificate and two or three published ports. It is a poor fit for a team that has no reason to operate a relay, and a poor fit if you need the upstream official documentation to match a newer derper, since the image pins its own build. Check three things before you deploy it: that your domain resolves to the host and that the ports are reachable, because DERP_DOMAIN is the only required setting and the container publishes 80, 443 and 3478 over UDP, that you understand the certificate modes, since letsencrypt is the default and the cert directory defaults to /app/certs, and that if you enable client verification you mount the Tailscale socket into the container, since the README says the container needs access to the local API through /var/run/tailscale/tailscaled.sock.

Frequently asked questions

What is derper-docker?

It is a Docker image for the Tailscale derper relay server, published as ghcr.io/kaaanata/derper for amd64 and arm64. The image builds the derper binary from source in a golang builder stage and ships it on Ubuntu, with every derper option exposed as an environment variable.

How do I run a Tailscale DERP server with this image?

Set DERP_DOMAIN to your own domain and run the container publishing ports 80, 443 and 3478 over UDP, for example docker run -e DERP_DOMAIN=derper.your-domain.com -p 80:80 -p 443:443 -p 3478:3478/udp ghcr.io/kaaanata/derper. The domain must resolve to the host.

What environment variables does derper-docker support?

DERP_DOMAIN is the only required one and holds the derper server hostname. The optional ones are DERP_CERT_DIR for storing Let's Encrypt certificates, defaulting to /app/certs, DERP_CERT_MODE with the options manual and letsencrypt, DERP_ADDR defaulting to :443, DERP_STUN and DERP_STUN_PORT for the STUN service on 3478, DERP_HTTP_PORT defaulting to 80 where -1 disables it, and DERP_VERIFY_CLIENTS with DERP_VERIFY_CLIENT_URL for client admission.

How does client verification work in derper-docker?

Setting DERP_VERIFY_CLIENTS makes the server verify clients through a local tailscaled instance, which means the container needs access to the Tailscale Local API, usually through /var/run/tailscale/tailscaled.sock. The README says that when running Tailscale bare metal on Linux, mounting that socket into the container should be enough.

Official sources

  1. Issues
  2. kaaanata/derper-docker on GitHub
  3. License: GPL-3.0
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/kaaanata-derper-docker.svg)](https://hysenlabs.com/projects/kaaanata-derper-docker)

Was this useful?