Comparison

DockTail vs ScaleTailA sidecar per app, or one host for all of them.

ScaleTail is a community project of 120+ ready-to-run Compose stacks, each with its own Tailscale sidecar and nothing to run besides Tailscale's image. DockTail is an agent that publishes the containers you already run as Tailscale Services from one host. Which one fits depends on whether you are setting up a new app or publishing one you already run.

ScaleTail: MIT · Compose templates · one tailnet device per app · DockTail: AGPL · Docker labels · one tagged host

How each one works

The same app, published twice.

Uptime Kuma on port 3001, reachable at https://uptime-kuma.<tailnet>.ts.net. On the left, ScaleTail's own stack for it. On the right, the DockTail equivalent.

ScaleTail

services/uptime-kuma/compose.yaml
configs:
  ts-serve:
    content: |
      {"TCP":{"443":{"HTTPS":true}},
      "Web":{"$${TS_CERT_DOMAIN}:443":
          {"Handlers":{"/":
          {"Proxy":"http://127.0.0.1:3001"}}}},
      "AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}}

services:
  tailscale:
    image: tailscale/tailscale:latest
    hostname: ${SERVICE}            # uptime-kuma, from .env
    environment:
      - TS_AUTHKEY=${TS_AUTHKEY}
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_SERVE_CONFIG=/config/serve.json
    configs:
      - source: ts-serve
        target: /config/serve.json
    volumes:
      - ./config:/config
      - ./ts/state:/var/lib/tailscale
    devices:
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - net_admin

  application:
    image: louislam/uptime-kuma:2
    network_mode: service:tailscale
Trimmed from the uptime-kuma stack; healthchecks, startup order and restart policy left out. The sidecar joins as a device named uptime-kuma, the app shares its network, and Serve proxies to 127.0.0.1:3001. Each app is a copy of this with its own .env and auth key.

DockTail

compose.yaml
services:
  docktail:                        # once per host
    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}

  uptime-kuma:
    image: louislam/uptime-kuma:2
    labels:
      - docktail.service.enable=true
      - docktail.service.name=uptime-kuma
      - docktail.service.port=3001
      - docktail.service.service-port=443
The agent runs once per host and talks to the host's tailscaled, which is tagged tag:server. Every further app is a few labels. service-port=443 makes it HTTPS; without it DockTail serves plain HTTP on port 80. See the DockTail quick start.

Key differences

Three things that decide it.

Tailnet shape

In ScaleTail every stack's sidecar joins the tailnet as its own device, with its own Tailscale IP, node key and entry on the Machines page. DockTail runs on one tagged host and advertises each app as a Tailscale Service (svc:uptime-kuma), so apps never become devices.

Where the config lives

ScaleTail keeps a .env and an inline Serve JSON per stack, applied with docker compose up -d --force-recreate, and nothing watches Docker. DockTail reads docktail.* labels, follows Docker events and reconciles again every 60 seconds by default.

What the tailnet needs

ScaleTail needs an auth key with MagicDNS and HTTPS turned on, and no policy file changes. DockTail needs a tagged host, tagOwners and autoApprovers.services in the policy file, and an OAuth client if it should create Service definitions for you.

Side by side

DockTail and ScaleTail.

Both publish an app on your tailnet with no ports on the Docker host. They differ in what joins the tailnet, where the config lives, and what each one asks of your tailnet policy.

DockTail ScaleTail
Native Tailscale Services ✅ One svc: name per app ❌ Tailscale Serve on a per-app device
Tailnet devices ✅ One per host ⚠️ One per app
Configuration ✅ Docker labels on containers you already run ⚠️ .env plus an inline Serve JSON per stack
Ready-made app recipes ❌ Bring your own Compose file ✅ 120+ stacks, each with its own README
Host port publishing ✅ None, proxies to the container IP ✅ None, the app shares the sidecar's network
HTTPS on the tailnet ⚠️ Opt in per service: service-port 443 or service-protocol https ✅ On in every template
Reacting to Docker changes ✅ Docker events plus a 60s reconcile ⚠️ Static stacks; Serve changes need a force-recreate
Tailnet policy changes ⚠️ Tags and Service auto-approvers ✅ None
Docker socket access ⚠️ Read-only mount required ✅ Not needed
Public access with Funnel ✅ Two labels plus the funnel node attribute; the public URL uses the host's name, since Funnel cannot attach to a Service ✅ Flip AllowFunnel; the public URL uses the app's name
Non-HTTP apps ⚠️ TCP and TLS-terminated TCP, one label set per port ✅ Any port on the device's Tailscale IP
Per-app admin console work ✅ First approval at most, depending on your policy ⚠️ Disable key expiry, delete the device on removal

ScaleTail column based on the project's own documentation as of October 2026. If we got something wrong, tell us.

Choose ScaleTail if

You want a recipe and nothing else running.

  • →You are setting up an app from scratch and want a tested recipe, with its database, secrets and healthchecks already wired.
  • →You want nothing on the host with access to the Docker socket, and no process besides Tailscale's own image.
  • →You can't or don't want to edit your tailnet policy file, or your devices are owned by a user rather than a tag.
  • →You want each app as its own device: its own Tailscale IP with every port on it reachable, its own ACL target, its own public Funnel name.

Choose DockTail if

Your stacks exist and should stay as they are.

  • →Your Compose files already exist and you would rather add a few labels than rebuild each stack around a sidecar.
  • →You want one tailnet device per host instead of one per app: fewer machines, node keys and key expiries to look after.
  • →You want exposure declared on the container and kept in sync from Docker events.
  • →You want Tailscale Services: svc: names and service tags, with the Service definitions created and kept in sync through the API once you give DockTail an OAuth client.

Both run side by side. DockTail only acts on containers with docktail.* labels, so a ScaleTail stack on the same host keeps its own sidecar and device.

Moving from ScaleTail

One stack at a time.

Nothing forces a big switch. Move the stacks you want on DockTail and leave the rest on ScaleTail. The app itself and its data folder stay the same; only the sidecar goes.

  1. 1

    Set up DockTail once on the host: tag it tag:server, add tagOwners and autoApprovers.services to the policy file, and create an OAuth client with Services: Write and Devices Core: Write, as in the Tailscale admin setup.

  2. 2

    Remove the ScaleTail stack first: docker compose down in its directory, then delete its device on the Machines page. The app data in ./<service>-data/ stays where it is.

  3. 3

    In compose.yaml, delete the tailscale service and the configs block, and remove network_mode: service:tailscale and the depends_on on it from the app.

  4. 4

    Add the labels. The port is the one in the old Proxy line (http://127.0.0.1:3001 means docktail.service.port=3001); add docktail.service.service-port=443 to keep HTTPS.

  5. 5

    Start it with docker compose up -d. If the stack published ports to your LAN, such as DNS on 53, move that ports block from the old sidecar to the app container.

Questions

Common ScaleTail questions.

Is ScaleTail still maintained?
Yes. New stacks and fixes were still landing in October 2026, and the catalog has grown past 120 apps. ScaleTail ships as a Git repository you clone and update with git pull; there are no versioned releases.
Does ScaleTail use Tailscale Services?
No. Each stack's sidecar joins the tailnet as its own device and runs Tailscale Serve on that device's name, configured through TS_SERVE_CONFIG. DockTail advertises each container as a Tailscale Service from one host, which is why that host needs a tag and Service auto-approvers in the policy file.
Does every ScaleTail app become its own Tailscale device?
Yes. The app shares the network namespace of its tailscale sidecar, and the sidecar is a device named after SERVICE in .env. ScaleTail's README asks you to disable key expiry for each one, and to delete the device by hand when you remove the stack.
What is a ScaleTail alternative without a sidecar per app?
DockTail runs one agent per host, reads docktail.* labels and publishes each container as a Tailscale Service through the host's tailscaled. You can also set up Tailscale Services by hand. TSDProxy is a single container too, but its default mode gives each app its own tailnet device (an opt-in Services mode is in its v3 beta); see DockTail vs TSDProxy.
Can I run ScaleTail and DockTail on the same host?
Yes. DockTail only acts on containers with docktail.* labels, and a ScaleTail stack stays its own device with its own sidecar. Keep ScaleTail stacks where the recipe saves you work and label the containers you already run.
Is ScaleTail an official Tailscale project?
It is a community project. The repository lives in Tailscale's tailscale-dev GitHub organization, it is listed on Tailscale's Community Projects page, and its README links a walkthrough from the Tailscale YouTube channel. It is MIT licensed and not a Tailscale product.
Open source · AGPL

Try it on a stack you already run.

Run the agent next to the stacks you already have, label one container, and leave your ScaleTail stacks where they are. The quick start covers the host and the tailnet policy.