Comparison

DockTail vs plain Tailscale Services
By hand, or from Docker labels.

Tailscale Services is Tailscale's own way to publish a resource under a stable svc: name, and DockTail is built on it. So this is not a rival product. It is about who operates the service: you, with the admin console and tailscale serve, or DockTail, from Docker labels.

Same svc: names, VIPs and MagicDNS · same tailscale serve CLI · one tagged host either way

How each one works

One nginx container, published twice.

Both end at http://myapp.your-tailnet.ts.net, served from the same tagged host. The difference is who types what.

Plain Tailscale Services

compose + shell + admin console
myapp:
  image: nginx:latest
  ports:
    - "127.0.0.1:8080:80"
# 1. admin console: Services, Advertise, Define a Service
#    name myapp · endpoint tcp:80 · tag tag:container

# 2. on the tagged host
sudo tailscale serve --service=svc:myapp --http=80 127.0.0.1:8080

# 3. approve under Service hosts, unless autoApprovers matches

# later, to take it down
sudo tailscale serve drain svc:myapp
sudo tailscale serve clear svc:myapp
Adapted from Tailscale's Services guide. You can skip the published port and point serve at the container IP from docker inspect, but nothing updates that IP when the container is recreated.

DockTail

docker-compose.yml
myapp:
  image: nginx:latest
  # no ports: DockTail proxies to the container IP
  labels:
    - docktail.service.enable=true
    - docktail.service.name=myapp
    - docktail.service.port=80
DockTail reads the container IP from Docker, creates svc:myapp through the Tailscale API when OAuth credentials are set, and runs the same tailscale serve --service command. Labels as in the label reference.

Where the config lives

By hand, one service is spread over three places: a definition in the admin console, a tailscale serve entry in tailscaled on the host, and a port or IP you keep track of. With DockTail it is three labels on the container. DockTail runs the same serve command and, with OAuth or API key credentials, creates the definition through the Tailscale API.

What happens when the container moves

Tailscale keeps the serve config across reboots, pointed at whatever target you typed. A container IP typed by hand goes stale when the container is recreated. DockTail reads the IP from Docker on container events and on a periodic reconcile (60 seconds by default) and updates the serve config.

What each one can publish

Plain Services covers anything a tagged host can reach: systemd units, VMs, a managed database as a remote target, UDP through a --tun endpoint, Go apps through tsnet. DockTail covers Docker containers only, over HTTP, HTTPS, TCP and TLS-terminated TCP.

Not automated

The Tailscale side is yours either way.

DockTail handles the per-container work. The policy file and the host identity stay with you, once, exactly as they would without DockTail. The Tailscale admin setup in the docs walks through each item.

  • Tag the host

    Service hosts must be tagged devices, for example tailscale up --advertise-tags=tag:server.

  • tagOwners

    The host's tag and the tag your services carry (DockTail's default is tag:container) have to be defined in the policy file.

  • autoApprovers.services

    Without a rule, each new service waits for an admin to approve its host. DockTail does not approve hosts.

  • Funnel node attribute

    Public Funnel needs the funnel nodeAttr on tag:server.

  • OAuth client

    Optional, and only DockTail needs it: Services: Write and Devices Core: Write, so it can create service definitions for you.

  • Access grants

    If your policy restricts who reaches what, clients need a grant to the svc: name or its tag. DockTail does not write policy.

Each labeled container becomes an ordinary Tailscale Service, so both setups put one tagged device in the admin console per host, not one per app. If you would rather give every app its own tailnet device, that is the sidecar approach compared in DockTail vs ScaleTail.

Side by side

DockTail and plain Tailscale Services.

The service on the tailnet is identical. The rows below are about what it takes to get it there and keep it pointed at the right container.

DockTail Plain Tailscale Services
Publishes native Tailscale Services (svc: name, VIP, MagicDNS) ✅ Same feature, through tailscale serve --service ✅
Extra software on the host ⚠️ One agent container with the Docker socket (read-only) and the tailscaled socket ✅ None beyond tailscaled
Per-app configuration ✅ Three docktail.* labels in the compose file ⚠️ A definition in the admin console plus a serve command on the host
Service definition in the control plane ✅ Created through the Tailscale API with OAuth or an API key; by hand without credentials ⚠️ By hand: admin console, API or Terraform
Host tag, tagOwners, autoApprovers ⚠️ One-time policy setup, yours ⚠️ One-time policy setup, yours
Reach a container without publishing a port ✅ Container IP, read from Docker ⚠️ Possible by typing the container IP, which nothing updates
Container recreated with a new IP ✅ Reconciled from Docker events and a periodic pass (60 s default) ⚠️ A typed container IP goes stale; a 127.0.0.1 port does not
Host reboot ✅ Reconciles from the running containers on start ✅ Serve config persists in tailscaled
App removed ✅ Stops advertising it when the container stops; deleting the definition is opt-in (DELETE_UNUSED_SERVICES) ⚠️ serve drain, serve clear, then delete the definition
Public access with Funnel ⚠️ Per container from two labels plus the funnel node attribute, on the host's own hostname ⚠️ tailscale funnel by hand, on the host's own hostname
Non-Docker workloads, remote targets, UDP via --tun ❌ Docker containers only ✅
Support ⚠️ Open-source community project (AGPL) on top of the official feature ✅ Official Tailscale feature, documented in the Tailscale KB

Plain Tailscale Services column based on Tailscale's own documentation and CLI source as of October 2026. If we got something wrong, tell us.

Choose plain Tailscale Services if

You have few services, or they are not containers.

  • →You publish a handful of services that rarely change, and would rather not run another container with Docker socket access.
  • →The workloads are not Docker containers: systemd units, VMs, a managed database as a remote target, Go apps with tsnet.
  • →You need a --tun endpoint for UDP, or text, file and unix socket targets.
  • →The tailnet lives in Terraform or GitOps and the admin console should stay authoritative. DockTail reverts hand edits to the tags and descriptions of the services it manages.

Choose DockTail if

The service should follow the container.

  • →Containers come and go, and you want the exposure written in the compose file next to the app.
  • →You do not want to publish host ports or look up container IPs.
  • →Stopping or removing a container should stop its service being advertised, without anyone running a command.
  • →You want Funnel for a container from a label instead of a separate tailscale funnel command.

Both can share a host. List your hand-made services in IGNORE_SERVICE_NAMES and DockTail never drains, clears or deletes them.

Switching

Handing a hand-made service to DockTail.

One service at a time. Start from the DockTail quick start for the agent itself.

  1. 1

    Keep the host tag, tagOwners and autoApprovers you already have. DockTail uses the same ones. If your definitions carry a tag other than tag:container, set docktail.tags or DEFAULT_SERVICE_TAGS to it, because labels win on every reconcile.

  2. 2

    Optional: create an OAuth client with Services: Write and Devices Core: Write so DockTail creates and updates definitions itself.

  3. 3

    Take down the hand-made entry for that service: tailscale serve drain svc:myapp, then tailscale serve clear svc:myapp.

  4. 4

    Start DockTail and add the docktail.service.* labels with the same service name. Drain and clear only removed the serve config, not the definition, so the service keeps its svc: name and MagicDNS name. Drop the 127.0.0.1 port mapping once DockTail reaches the container directly.

Questions

Tailscale Services with and without DockTail.

Does DockTail replace Tailscale Services?
No, it uses them. For every labeled container DockTail runs tailscale serve --service=svc:myapp (the name from its labels) on the host and, with OAuth or API key credentials, creates the service definition through the Tailscale API. The service, its virtual IP, its MagicDNS name and its approval are all Tailscale's.
How do I expose a Docker container as a Tailscale Service without DockTail?
Define the service in the admin console (name, a tcp: endpoint, a tag), publish the container on a loopback port such as 127.0.0.1:8080:80, and run tailscale serve --service=svc:myapp --http=80 127.0.0.1:8080 on a tagged host. Then approve the host under Service hosts, or let an autoApprovers rule do it. Tailscale's guide has the details.
Does tailscale serve --service survive a reboot or a container restart?
The serve config survives a reboot: with Services, tailscale serve runs in background mode and tailscaled resumes it. It does not follow a container, though. If you pointed it at the container's IP, re-run it whenever the container is recreated with a new address. A target on 127.0.0.1 avoids that, at the cost of a published host port per app.
Can I use Tailscale Funnel with a Tailscale Service?
Not directly. The Tailscale CLI rejects --service together with Funnel, and the feature request is still open. Funnel runs on the host's own hostname instead, which is also how DockTail exposes it: the public URL is https://<host>.<tailnet>.ts.net, not the service name.
Do I still need tags and auto-approvers with DockTail?
Yes. A tagged host, the tags in tagOwners, an autoApprovers.services rule and, for Funnel, the funnel node attribute are one-time policy setup that DockTail does not write. The Tailscale admin setup in the docs lists each one.
Does DockTail add features to Tailscale Services?
No. Every labeled container becomes an ordinary Tailscale Service, so Tailscale's rules for Services apply the same way with or without DockTail: the host must be tagged, the host needs approval, and Funnel cannot attach to a svc: name. What DockTail changes is who runs the commands, and that the target follows the container.
Open source · AGPL

Hand one service to DockTail.

Keep the tags and auto-approvers you already have, add DockTail next to one app, and give it three labels. Your hand-made services keep running beside it.