Reference
Use this section when checking exact configuration names, defaults, and supported protocols.
Environment Variables
| Variable | Default | Description |
|---|---|---|
TAILSCALE_OAUTH_CLIENT_ID |
- | OAuth client ID. Enables automatic service creation when paired with the secret. |
TAILSCALE_OAUTH_CLIENT_SECRET |
- | OAuth client secret. Enables automatic service creation when paired with the client ID. |
TAILSCALE_API_KEY |
- | API key alternative to OAuth. |
TAILSCALE_TAILNET |
- |
Tailnet ID. Defaults to the credential's tailnet. |
DEFAULT_SERVICE_TAGS |
tag:container |
Default tags for services whose containers set no docktail.tags label. Tags are reconciled on every cycle; manual tag edits in the admin console are overwritten. |
IGNORE_SERVICE_NAMES |
- | Comma-separated service names DockTail must not drain, clear, delete, or restore missing advertisements for during reconciliation or shutdown cleanup. |
DELETE_UNUSED_SERVICES |
false |
When true, DockTail deletes tailnet Service definitions that no host advertises anymore. Requires API credentials. See Cleanup Behavior. |
SKIP_SHUTDOWN_CLEANUP |
false |
When true, DockTail leaves its services and Funnels advertised on shutdown instead of draining and clearing them. This can keep ports exposed on the tailnet beyond what your current labels define; see Cleanup Behavior. |
LOG_LEVEL |
info |
Logging level for all output, including the DockTail Cloud module: debug, info, warn, or error. Any other value means info. |
RECONCILE_INTERVAL |
60s |
State reconciliation interval. |
DOCKER_HOST |
unix:///var/run/docker.sock |
Docker daemon socket. Rootless Docker typically uses unix:///run/user/<uid>/docker.sock. |
TAILSCALE_SOCKET |
/var/run/tailscale/tailscaled.sock |
The tailscaled socket DockTail checks: the startup missing-socket hint, the socket-loss check, and the image's health check. DockTail does not pass it to the bundled tailscale CLI, which does the serve and Funnel work at its own default of /var/run/tailscale/tailscaled.sock, so mount the daemon's socket directory at /var/run/tailscale either way. |
EXIT_ON_SOCKET_LOSS |
true |
When true, DockTail exits if the Tailscale socket stays unreachable past the grace period, so the container's restart policy can re-establish the mount. See Tailscale Socket Loss. |
SOCKET_LOSS_GRACE_PERIOD |
90s |
How long the Tailscale socket may stay unreachable before DockTail exits. Must be longer than a normal tailscaled restart. |
UPDATE_CHECK |
true |
When true, DockTail checks once a day whether a newer release exists and logs it once. Set false to never contact GitHub. See Version And Updates. |
HEALTH_FILE |
/tmp/docktail-health.json |
Where DockTail writes the status file that docktail health (the image's health check) reads. Point it at a writable path if the container's /tmp is read-only. See Health Check. |
If both OAuth and API key credentials are configured, DockTail uses OAuth.
Sensitive credential variables can also be loaded from files. This is useful with Docker secrets, Swarm secrets, and other secret mounts. Direct environment variables take precedence; when the direct variable is unset, DockTail checks FILE__VARIABLE_NAME first and VARIABLE_NAME_FILE second. The file content is used as the value with trailing newlines removed.
Supported file-backed credential variables:
| Direct variable | File variable alternatives |
|---|---|
TAILSCALE_OAUTH_CLIENT_ID |
FILE__TAILSCALE_OAUTH_CLIENT_ID, TAILSCALE_OAUTH_CLIENT_ID_FILE |
TAILSCALE_OAUTH_CLIENT_SECRET |
FILE__TAILSCALE_OAUTH_CLIENT_SECRET, TAILSCALE_OAUTH_CLIENT_SECRET_FILE |
TAILSCALE_API_KEY |
FILE__TAILSCALE_API_KEY, TAILSCALE_API_KEY_FILE |
IGNORE_SERVICE_NAMES accepts bare names like grafana and fully qualified names like svc:grafana.
Durations (RECONCILE_INTERVAL, SOCKET_LOSS_GRACE_PERIOD) use Go syntax such as 30s, 5m or 1h30m; booleans accept true/false, 1/0, and t/f. An unparseable value in one of the variables above logs a warning and falls back to the default.
Docker Connection And Log Output
DockTail builds its Docker client from the standard Docker environment variables, so they work as they do for the docker CLI. Besides DOCKER_HOST above, which also takes tcp://host:port (for example a read-only socket proxy):
| Variable | Default | Description |
|---|---|---|
DOCKER_API_VERSION |
negotiated | Pins the Docker API version instead of negotiating it with the daemon. |
DOCKER_CERT_PATH |
- | Directory with ca.pem, cert.pem and key.pem. Setting it makes DockTail talk TLS to a tcp:// daemon. |
DOCKER_TLS_VERIFY |
- | Used with DOCKER_CERT_PATH: any non-empty value verifies the daemon's certificate; unset or empty skips verification. |
Log lines are colored only when stdout is a terminal; setting NO_COLOR (to any value) or TERM=dumb turns color off there too.
TAILSCALE_AUTH_KEY in the examples is read by the tailscale/tailscale sidecar container (as TS_AUTHKEY), not by DockTail.
DockTail Cloud (optional)
These variables enable optional DockTail Cloud reporting. They are opt-in: the agent is completely inert unless DOCKTAIL_CLOUD_KEY is set. See DockTail Cloud.
| Variable | Default | Description |
|---|---|---|
DOCKTAIL_CLOUD_KEY |
- | Workspace key (dtc_...) from the cloud dashboard. Enables reporting. Inert when unset. |
DOCKTAIL_LOG_LEVEL |
info |
Read, but currently has no effect: the cloud module logs at the level LOG_LEVEL sets. |
DOCKTAIL_CHECK_INTERVAL |
30s |
How often local-vantage checks run (5s–5m). A value outside that range, or one that does not parse, keeps the cloud module from starting; DockTail itself keeps running. |
DOCKTAIL_HOST_ROOT |
/host |
Where the host's root filesystem is bind-mounted, for whole-host disk usage. Only used when that path exists; see Disk Usage. |
Local-development overrides: DOCKTAIL_CLOUD_URL replaces the built-in ingest
endpoint. ws:// is allowed for loopback endpoints; non-loopback plaintext
requires DOCKTAIL_CLOUD_ALLOW_INSECURE=true and must never be used in
production. Non-loopback production endpoints must use wss://.
Supported Protocols
Tailscale-facing docktail.service.service-protocol values:
| Value | Description |
|---|---|
http |
Layer 7 HTTP. |
https |
Layer 7 HTTPS with automatic TLS. |
tcp |
Layer 4 TCP. |
tls-terminated-tcp |
Layer 4 TCP; Tailscale terminates incoming TLS and forwards decrypted TCP to the backend. |
docktail.service.path selects the HTTP(S) Serve mount point and defaults to /. It must start with / and is rejected for TCP protocols.
docktail.service.proxy-protocol (1 or 2) is valid only with tcp or tls-terminated-tcp. It is off by default because the backend must understand the PROXY header. HTTP/HTTPS values are rejected.
Container-facing docktail.service.protocol values:
| Value | Description |
|---|---|
http |
HTTP backend. |
https |
HTTPS backend with a valid certificate. |
https+insecure |
HTTPS backend with a self-signed certificate. |
tcp |
TCP backend. |
tls-terminated-tcp |
TCP backend with TLS termination. |
Funnel docktail.funnel.protocol values:
| Value | Description |
|---|---|
http |
HTTP Funnel. |
https |
HTTPS Funnel. |
tcp |
TCP Funnel. |
tls-terminated-tcp |
TLS-terminated TCP Funnel. |
docktail.funnel.path is supported only with HTTP(S) Funnel protocols. It defaults to / and must start with /.
Cleanup Behavior
DockTail attempts to clean up the services and Funnels it advertises locally when it shuts down, reducing stale exposure while it is not running. Service cleanup runs serially, once per service name even when it has several ports: drain first, then clear. Draining stops new connections; DockTail clears the configuration immediately afterward without waiting for all existing connections to finish.
Cleanup has an 8-second budget, leaving headroom within Docker’s default 10-second stop timeout. Cleanup stops scheduling commands when its budget expires. A crash, SIGKILL, daemon error, or the cleanup deadline can leave some services configured or advertised; reconciliation repairs missing advertisements when DockTail resumes.
On startup and every reconciliation cycle, DockTail also reads tailscale debug prefs. If a desired service has matching Serve configuration but is missing from AdvertiseServices, DockTail restores its advertisement with tailscale serve advertise after syncing its control-plane definition (when API sync is enabled). This repairs services left drained but configured after interrupted cleanup without rewriting their endpoints. It uses the same local daemon socket as Serve, requires no additional API credentials, and runs once per service name. Ignored services and service names with endpoint conflicts are excluded from this repair. Failed, malformed, or unsupported prefs reads are treated as unknown state: DockTail skips advertisement repair for that cycle while continuing normal configuration reconciliation.
A manual tailscale serve drain of a desired, matching service is therefore reversed on the next successful reconciliation. Stop the backing container, remove its service labels, or exclude the service from advertisement repair with IGNORE_SERVICE_NAMES if the drain should persist. Local configuration and advertisement do not guarantee that Tailscale has propagated peer routes or that the service is reachable.
SKIP_SHUTDOWN_CLEANUP=true disables this cleanup. DockTail then leaves its services and Funnels advertised when it exits instead of tearing them down. It affects only graceful shutdown; a hard crash never runs cleanup either way.
Warning: Enabling this keeps ports exposed on the tailnet while DockTail is down, potentially beyond what your current labels define. The serve and Funnel configuration lives in
tailscaled, not in DockTail, so anything DockTail last advertised keeps serving on the host until DockTail comes back. If you stop a container, remove it, or delete its DockTail labels while DockTail is not running, that service (and any Funnel) stays reachable for the entire downtime and is only reconciled away once DockTail restarts. Only enableSKIP_SHUTDOWN_CLEANUPif keeping services reachable across DockTail restarts is worth deliberately skipping cleanup, and treat the advertised surface as detached from your labels until DockTail is running again.
On restart, DockTail re-adopts the still-advertised services and removes only those whose containers are no longer running. Ignored services (IGNORE_SERVICE_NAMES) are never cleaned up regardless of this setting.
By default DockTail does not delete Tailscale Service definitions from the Control Plane when containers stop; this is a conservative strategy that avoids removing definitions unexpectedly.
Deleting unused Service definitions
Set DELETE_UNUSED_SERVICES=true to let DockTail remove Service definitions that are no longer advertised by any host. This requires API credentials (OAuth or API key). It is disabled by default.
During each reconciliation, for every Service definition in the tailnet DockTail:
- Keeps the Service if DockTail currently advertises it (it is backed by a running container).
- Keeps the Service if its name is listed in
IGNORE_SERVICE_NAMES. - Asks the Control Plane which hosts are registered for the Service. If at least one host is registered, DockTail keeps it.
- Deletes the Service only when no host is registered for it.
Because the decision is based on the tailnet-wide host list, this is safe to enable on multiple DockTail instances at once: a Service hosted by any other host or instance always reports at least one host and is never deleted. DockTail also skips deletion whenever an API call fails, so it never deletes under uncertainty.
Note: That host list is a configuration and approval registry, not a liveness signal. A host stays listed while it is configured to host the Service, and remains listed for some time after it stops advertising it. Cleanup can therefore lag behind reality — it may keep a definition longer than expected, but it will not delete one that is still in use.
This cleanup runs only during reconciliation, not during shutdown, so restarting DockTail does not delete and recreate the Services of still-running containers.
Note: When enabled, DockTail may also delete Service definitions it did not create if they have no advertising hosts (for example, a Service you defined in the admin console but never advertised). Add such names to
IGNORE_SERVICE_NAMESto protect them.
Tailscale Socket Loss
DockTail talks to tailscaled over a Unix socket that it bind-mounts from the
host or shares with a sidecar. That mount is resolved once, when the container
starts.
This matters whenever tailscaled restarts. On a host install under systemd, the
unit declares RuntimeDirectory=tailscale and leaves RuntimeDirectoryPreserve
at its default of no, so systemd removes /run/tailscale when the daemon
stops and creates a new directory when it starts. A container that mounted the
old directory stays attached to it after it is unlinked, and the new socket never
becomes visible inside the container. Sharing the socket through a host path with
a sidecar that gets recreated has the same effect. Retrying cannot help: the
socket is not late, it is in a directory this container can no longer see.
An upgrade is the most common trigger, because upgrading the package restarts the
daemon — but it is not the only one. Measured on Debian 12 (systemd 252,
tailscale 1.102.2), an ordinary systemctl restart tailscaled replaces the
directory just as an upgrade does, and so does any stop/start pair:
| directory | DockTail's mount | |
|---|---|---|
| before | inode 264, socket present | inode 264, socket present |
after systemctl restart tailscaled |
inode 412, socket present | inode 264, empty |
| after the container restarts | inode 412, socket present | inode 412, socket present |
Because of this, mounting the directory rather than the socket file — which is what the setup guide recommends, and which does survive the socket file being recreated — is not on its own enough to survive a daemon restart on a systemd host.
Without intervention DockTail would keep running in that state — process alive, every Tailscale call failing, and every Service it manages drifting until it goes offline, with no recovery until someone restarts the container by hand.
So DockTail probes the socket, and if it stays unreachable for
SOCKET_LOSS_GRACE_PERIOD (default 90s) it logs the reason and exits, letting
the container's restart policy re-create the container and with it the mount.
- Use a restart policy.
restart: unless-stopped(oralways) is what turns the exit into a recovery. Without one, DockTail stops instead of restarting. - The grace period must stay comfortably longer than a normal
tailscaledrestart, which takes a second or two. Brief outages are ignored and never cause an exit. - The check arms only after the socket has been reachable at least once, so
starting DockTail before
tailscaledwaits rather than exits. At startup DockTail also waits up to 15 seconds for the socket before its first reconcile, so a sidecar that is still starting does not delay your Services by a wholeRECONCILE_INTERVAL. - Set
EXIT_ON_SOCKET_LOSS=falseto disable it and keep the old behaviour of retrying forever.
Prefer a named volume over a host path when you run tailscaled as a sidecar: a
volume keeps one directory for its lifetime, so recreating the sidecar cannot
detach DockTail's mount in the first place.
Version And Updates
DockTail logs its version on startup (Starting DockTail version=…), and the
binary prints it on request:
docker exec docktail /app/docktail --version
# or, without a running container:
docker run --rm ghcr.io/marvinvr/docktail:latest --version
Include that version in bug reports.
About 30 seconds after startup and then once a day (hourly after a failed check), a release build asks the GitHub API for the repository's tags and, when a newer stable release exists, logs it once:
INF A newer DockTail release is available; pull the new image and recreate the container to update (set UPDATE_CHECK=false to stop checking) current=1.8.2 latest=1.8.3 release_notes=https://github.com/marvinvr/docktail/releases
The request is an anonymous GET to api.github.com that carries nothing
about your installation beyond the docktail/<version> user agent (GitHub
sees the source IP, as with any request). It is
skipped for development builds, a failure is only logged at debug level, and
UPDATE_CHECK=false turns it off. Pre-release tags are never offered, and a
pre-release build newer than the latest stable release is not nagged.
Health Check
The image's HEALTHCHECK runs docktail health, which tracks DockTail itself
rather than only the Tailscale daemon. The running process rewrites a small
JSON status file every 10 seconds (and after every reconcile) at HEALTH_FILE;
docktail health reads it, prints a one-line verdict, and exits 0 when
healthy and 1 when not. Nothing listens on a port.
DockTail is unhealthy when:
- the status file is missing or has not been updated for a minute (DockTail is hung, stopped, or cannot write the file);
- the
tailscaledsocket does not accept connections; - the last two reconciles could not read the containers from Docker;
- no reconcile has finished for three reconcile intervals (at least three minutes), meaning the loop is stuck.
A reconcile that fails for individual services — a label conflict, a service
the tailnet refuses — is reported as healthy, with errors: that is one
container's configuration, and DockTail keeps serving the rest.
The DockTail Cloud link state (connecting,
connected, disconnected, rejected, or failed) is included in the output
but never makes the container unhealthy: a cloud outage or a revoked key does
not stop DockTail from serving containers, and a restart triggered by the
health status (Swarm, autoheal) would drain every service without fixing it.
docker exec docktail /app/docktail health
# healthy: last reconcile 12s ago; cloud connected for 3h12m4s
docker inspect --format '{{json .State.Health}}' docktail # Docker's view, with recent outputs
docker exec docktail cat /tmp/docktail-health.json # the full status
With a read-only root filesystem, mount a tmpfs at /tmp or set
HEALTH_FILE to a writable path; otherwise DockTail logs a warning and the
health check reports unhealthy.
Useful Links
- Tailscale Services documentation:
https://tailscale.com/kb/1552/tailscale-services - Tailscale Funnel documentation:
https://tailscale.com/kb/1311/tailscale-funnel - Tailscale service configuration reference:
https://tailscale.com/kb/1589/tailscale-services-configuration-file - Docker SDK for Go:
https://docs.docker.com/engine/api/sdk/