Comparison

DockTail vs TSDProxy
A machine per app, or a Service per app.

TSDProxy puts Docker containers on a tailnet with a couple of labels and no Tailscale install on the host, and it ships its own web dashboard. This page is about the main design difference: TSDProxy v2 gives every container its own Tailscale machine, while DockTail serves each one as a Tailscale Service from the host.

TSDProxy v2.3.4 stable · v3.0.0-beta.4 adds an opt-in Services mode · MIT · as of October 2026

How each one works

Same nginx, two compose files.

Both watch the Docker socket and react to labels. The difference is what they create on the tailnet when a labelled container starts.

TSDProxy v2 quick start docker-compose.yml
services:
  tsdproxy:
    image: almeidapaulopt/tsdproxy:2
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - tsdproxy-data:/data
      - ./config:/config
    ports:
      - "8080:8080"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped

  myapp:
    image: nginx:alpine
    labels:
      tsdproxy.enable: "true"
      tsdproxy.name: "myapp"

volumes:
  tsdproxy-data:
From the TSDProxy README. TSDProxy writes a default tsdproxy.yaml, you log the new node in from the dashboard (or configure an auth key or OAuth client), and myapp appears as a new machine at https://myapp.<tailnet>.ts.net.
DockTail quick start docker-compose.yml
services:
  docktail:
    image: ghcr.io/marvinvr/docktail:latest
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /var/run/tailscale:/var/run/tailscale
    environment:
      - TAILSCALE_OAUTH_CLIENT_ID=${TAILSCALE_OAUTH_CLIENT_ID}
      - TAILSCALE_OAUTH_CLIENT_SECRET=${TAILSCALE_OAUTH_CLIENT_SECRET}

  myapp:
    image: nginx:latest
    # No ports needed. DockTail proxies to the container IP.
    labels:
      - docktail.service.enable=true
      - docktail.service.name=myapp
      - docktail.service.port=80
From the DockTail quick start. The host must already be on the tailnet with tag:server and allowed to advertise Services (tags and auto-approvers in the Tailscale admin setup). myapp becomes a Service at myapp.<tailnet>.ts.net, not a new machine.

What TSDProxy v3 changes

The v3 beta keeps the per-machine mode as its default and adds a Services mode (services: true): one shared tsnet node, one Tailscale Service per app, OAuth required. That mode has no Funnel, no UDP, no custom domains and no HTTP redirects. In the v3 source we read, releasing a container's last port also deletes its Service definition from the tailnet. Several rows on this page will need another look when v3 goes stable.

Key differences

Where the two designs part.

Both read labels and both end at a ts.net name. What sits in between is different.

A machine per app, or a Service per app

TSDProxy v2 starts an embedded tsnet node for every labelled container, so each app is its own machine in the admin console with its own node key. DockTail advertises each app as a Tailscale Service (a svc: name with its own VIP) from the one tagged host it runs on. TSDProxy v3, still in beta, adds an opt-in Services mode that works the same way.

Tailscale inside the proxy, or on the host

TSDProxy carries Tailscale in its own process and proxies every request itself, so the Docker host needs no Tailscale install. DockTail drives the host's tailscaled (or a Tailscale sidecar) through its serve config, which then forwards to the container IP. The agent itself keeps no state.

A toolbox, or a reconciler

TSDProxy ships a web dashboard, health checks, webhooks, Prometheus metrics, UDP, port ranges and non-Docker targets. DockTail does one job: turn docktail.* labels into Services and Funnels, with paths and PROXY protocol. Monitoring is the separate, optional DockTail Cloud.

Side by side

DockTail and TSDProxy, row by row.

The TSDProxy column describes stable v2.3.4 unless a cell names v3. Several rows read differently for a v3 provider in Services mode.

DockTail TSDProxy
How an app appears on the tailnet ✅ A Tailscale Service, advertised by the host ⚠️ v2.3.4: its own tsnet machine. v3 beta: opt-in Services mode
Tailnet machines for ten labelled apps ✅ One: the host or its Tailscale sidecar ⚠️ Ten in the default mode, one in v3 Shared or Services mode
Tailscale on the Docker host ⚠️ Required: host tailscaled on Linux, or a Tailscale sidecar ✅ Not needed, Tailscale is embedded via tsnet
Reaches apps without published ports ✅ Proxies to the container IP by default ⚠️ Default host.docker.internal setup needs published ports; sharing the app's network avoids it
Tailscale Funnel ✅ On the host's hostname, by port or path, next to private Services ⚠️ Own public hostname per app in the default mode; not in v3 Services mode
Protocols ⚠️ HTTP, HTTPS, TCP, TLS-terminated TCP ✅ HTTP, HTTPS, TCP, UDP and port ranges (no UDP in Services mode)
Serve an app under a URL path ✅ docktail.service.path and docktail.funnel.path ❌ No path-based routing, per its docs
State to persist ✅ None in the agent; Tailscale state stays in tailscaled ⚠️ Node keys under /data; losing it registers new machines
Dashboard, health checks, alerts ⚠️ Not in the agent; optional hosted DockTail Cloud ✅ Web dashboard, health checks, webhooks, Prometheus metrics
Targets beyond the local Docker daemon ❌ The Docker daemon of the host it runs on, one agent per host ✅ Remote Docker daemons, YAML lists, Incus and Proxmox (v3)
License — AGPL v3 — MIT

TSDProxy column based on the project's own documentation and source as of October 2026, v3.0.0-beta.4. Funnel cannot be attached to a Tailscale Service, which is why DockTail serves Funnels on the host's own hostname and TSDProxy's Services mode has none. If we got something wrong, tell us.

Choose TSDProxy if

You want one self-contained tool with a UI.

  • →You would rather not install Tailscale on the Docker host or run a sidecar next to it.
  • →You want a dashboard, health checks, webhook alerts and Prometheus metrics in the same free tool.
  • →You need UDP or port ranges, or every public app on its own Funnel hostname.
  • →You proxy NAS boxes, Proxmox VMs, Incus instances or several Docker hosts from one place, or your tailnet runs on Headscale.

Choose DockTail if

You want Services from the host, with nothing extra to keep.

  • →Each app should be a Tailscale Service on a stable release, not one more machine with its own node key.
  • →You do not want to publish ports or attach a proxy to every app network.
  • →You want a stateless agent, with no /data volume to back up and no duplicate machines after losing it.
  • →You need private Services and a Funnel from the same host, or apps mounted under a URL path.

They can share a host: TSDProxy reads tsdproxy.* labels and DockTail reads docktail.*, so a UDP app can stay on TSDProxy while the rest move.

Moving from TSDProxy

From per-app machines to Services.

Most of the work is on the Tailscale side, once. The admin setup is in the DockTail docs.

  1. 1

    Tag the Docker host (sudo tailscale up --advertise-tags=tag:server --reset) or use the Tailscale sidecar, then add the tagOwners and autoApprovers.services rules from the DockTail docs, plus the funnel node attribute on tag:server if you use Funnel.

  2. 2

    Create an OAuth client scoped to tag:server with Services: Write and Devices Core: Write (plus Auth Keys: Write for the sidecar setup), and pass it to DockTail.

  3. 3

    Translate the labels. tsdproxy.enable and tsdproxy.name become docktail.service.enable and docktail.service.name. A port like 443/https:80/http becomes docktail.service.port=80 plus docktail.service.service-port=443. A tailscale_funnel port becomes docktail.funnel.enable=true plus docktail.funnel.port, and its public URL moves to the host's hostname. UDP and port ranges have no DockTail equivalent.

  4. 4

    Stop TSDProxy and delete its per-app machines in the admin console before DockTail creates Services with the same names. TSDProxy's v3 docs report that Tailscale rejects a Service whose name a device still uses (a 409 "name is in use" error).

  5. 5

    Start DockTail. Approve each new Service once if your ACL does not auto-approve it, then remove TSDProxy's /data volume.

Questions

DockTail next to TSDProxy.

Is TSDProxy still maintained?
Yes. It has one main maintainer. The current stable release is v2.3.4 (June 2026), and v3.0.0-beta.4 followed on 30 September 2026. Before the v2 betas in spring 2026, the last release was v1.4.7 in March 2025.
Does TSDProxy use Tailscale Services?
Only in v3, which is still in beta, and only when a Tailscale provider sets services: true with OAuth credentials. That mode has no Funnel, no UDP and no custom domains. Stable v2 and the v3 default mode give each container its own Tailscale machine.
Does TSDProxy create a Tailscale device for every container?
In its default mode, yes. Each proxied container gets an embedded tsnet node with its own hostname, certificate and node state under /data. v3 adds Shared and Services modes that put many apps behind one machine.
Does TSDProxy need published ports?
With its default setup, yes. TSDProxy reaches apps through host.docker.internal, and its docs say target containers must then publish their ports. If TSDProxy joins the app's Docker network and tryDockerInternalNetwork is on, it reaches the container IP directly.
What is a TSDProxy alternative that does not add a machine per container?
DockTail advertises every labelled container as a Tailscale Service from one tagged host, so the admin console shows one machine per host. Within TSDProxy, the v3 beta Services mode does the same. tsbridge follows the same one-node-per-app model as TSDProxy v2.
Can I run TSDProxy and DockTail on the same host?
Yes. They read different labels (tsdproxy.* and docktail.*), so each container is picked up by only one of them. Give them different names, so a TSDProxy machine and a DockTail Service never compete for the same name.
Open source · AGPL v3

Try it on one container.

Add three docktail.* labels to a container TSDProxy does not manage and see it show up as a Service. Your TSDProxy machines stay as they are.