TailgateDeploy it

Open-source overlay network

Run your own things privately. We did not write the VPN.

Tailgate runs the official Tailscale clients against your own Headscale server and your own relays. It adds the rest: checked config, pinned builds, private HTTPS, and a console that decides which device reaches which service.

  • Official Tailscale clients
  • Upstream Headscale 0.29.4
  • Relays you own
  • Deny-all by default
Low view of an open wooden farm gate at night under a red sun; glowing bubbles joined by dotted lines stream up through the gate past a yellow bottle cap.

Watch it work

The stock app signs in to your server. Three devices, all yours. From the public internet: nothing.

Terminal session (illustrative). The stock Tailscale app signs in to your own server, lists your three devices, reaches a private app over HTTPS, and is refused from the public internet.
# the stock app signs in to your server
tailscale login --login-server=https://tailgate.example.com
Success.
# three devices, all yours
tailscale status
100.120.0.7    laptop    macOS
100.120.0.10   homelab   linux
100.120.0.11   nas       linux
# a private app with a real certificate
curl -sI https://photos.apps.example.com
HTTP/2 200
# from the public internet: nothing
curl -m 5 https://photos.apps.example.com
curl: (28) Connection timed out after 5001 ms

Why upstream

No new VPN code to audit.

The hard parts already exist. Tailgate does not rewrite them. It runs them as they ship, pins the exact versions and checks the checksums. You only have to read the code Tailgate adds, and there is not much of it.

Three tall sealed glass bottles with red wax seals and blank tags, bubbles rising, a red sun behind the middle one.

Upstream, unmodified

The official Tailscale clients on every device. Headscale 0.29.4 for control, and derper built from the locked Tailscale source. No forks and no patches.

Run by you

The control plane and the relays run on hosts you choose. There is no hosted coordinator, and no relay map is fetched from Tailscale.

What Tailgate adds

Validated config, pinned and checksummed artifacts, private HTTPS, and a small console for access. No networking code. No crypto.

Deploy

Two steps. That is the aim.

Write down what you want in one file. Tailgate checks it before it touches anything. Then it puts the pieces on your host.

A sheet of paper blows at the foot of a path that climbs a hill past a red and a blue stepping stone to a cabin with two relay towers, under a red sun.
  1. Describe and check

    Name your domain, relays, devices and services in one file, and Tailgate tells you whether it holds together.

    $ python3 tools/tailgate.py validate tailgate.json
  2. Install on your host

    Headscale, the relays, the certificates and the console go onto the host you chose.

    Installer coming soon Until then, the bundle is rendered and installed by hand.

  3. You bring a host, a domain, and DNS that can answer a certificate challenge.

Terminal session (real output). Validating and rendering the example pilot configuration with tailgate.py: the input is valid and a bundle is rendered without installing anything.
# describe it and check it
python3 tools/tailgate.py validate examples/pilot.json
Input valid
# render the bundle; nothing is installed
python3 tools/tailgate.py render examples/pilot.json \
    --output build/pilot
Bundle rendered
ls build/pilot
README.txt             headscale.json   records.json
controller.nginx.conf  manifest.json    tailgate.service
derp.json              policy.json      tailgate-exit.service

Private apps

HTTPS only your devices can reach.

Each service listens on its overlay address, port 443, with a publicly trusted certificate from a DNS challenge. Browsers trust it and the public internet cannot reach it. There is no public listener to find.

Through the bars of a locked iron garden gate with a glowing keyhole, a cottage with one lit window under a red moon.
Terminal session (real output). Validating and rendering a private application plan with applications.py, which prints a plan hash and writes compose, nginx and service files.
# check the app list, then render it
python3 tools/applications.py validate \
    examples/application.json \
    examples/application-platform.json
Valid application and platform
python3 tools/applications.py render … \
    --output build/sample-app
95b4b222363fdc099c34306202a1c62c…
# same input, same plan hash
ls build/sample-app
README.txt    plan.json           private-service.json
compose.yaml  private-nginx.conf

Connection path

Direct when it can be. Your relay when it can't.

Devices talk to each other directly, encrypted with WireGuard under the Tailscale client. When there is no direct road, the packets go through a relay you own, still encrypted. The control plane makes introductions. It does not carry traffic.

Terminal session (illustrative). tailscale ping reaches the homelab first through your own relay, then directly once the two devices find each other.
# first through your own relay
tailscale ping homelab
pong from homelab via DERP(tg-fra) in 41ms
pong from homelab via DERP(tg-fra) in 39ms
# then the two devices find each other
pong from homelab via 203.0.113.24:41641 in 6ms
Two islands on a blue sea under a red sun: an arc of bubbles leaps directly between them, and a dotted path detours through a red-and-white lighthouse on a rock in between.

Access

Joining is not access.

A device that joins gets an address and nothing else. You grant it each service and port in the console. Signing in with Google proves who you are. It does not open anything.

  • The policy starts empty: {"grants":[]}.
  • Each grant names a device, a service and a port.
  • Every new device needs its own approval.
  • You can revoke any grant.

Coexistence

Keep hosted Tailscale where it is.

A server can stay on hosted Tailscale and run a second Tailgate client in its own network namespace, and the two stay apart. An optional gateway bridges only the users you list. An empty list means nobody.

ONE SERVERhost tailscaledhosted Tailscale100.64.0.0/10netns tailgateTailgate client100.120.0.0/16host sshd:22 only

Open source

Read what it does.

Tailgate is open source. The part it adds is small, and it is meant to be read.

  • Read every line Tailgate adds.
  • Upstream is pinned by version and checksum.
  • tailgate-core holds the components and tests. tailgate-deploy holds your config and pins core by exact revision.

Public repository coming soon

A magnifying glass held over the open neck of a mint glass bottle as bubbles burst out and a yellow bottle cap flies off, red sun behind.

Limits

What it does not do.

No host provisioning

You bring the servers and the cloud accounts. Tailgate configures what is already there.

No new protocol

It runs Tailscale's protocol through upstream code and does not replace it.

Not every OS certified

The official clients run on many systems, and Tailgate does not certify each one.

No secrets in the bundle

The rendered bundle holds references to secrets, never the values, so the values stay with you.