Server configuration
TSDProxy utilizes the configuration file /config/tsdproxy.yaml for its settings.
The config file path can be overridden using the --config CLI flag:
tsdproxy --config /path/to/tsdproxy.yamlImportant
Environment variable configurations used in versions prior to v0.6.0 are
deprecated and will be removed in future releases. However, some legacy env vars are still read during initial config generation: DOCKER_HOST, TSDPROXY_HOSTNAME, TSDPROXY_AUTHKEY, TSDPROXY_AUTHKEYFILE, TSDPROXY_CONTROLURL, and TSDPROXY_DATADIR.
Sample Configuration File
Warning
Configuration files are case-sensitive.
defaultProxyProvider: default
defaultDNSProvider: cloudflare
defaultTLSProvider: acme
cleanupDNS: true
dnsProviders:
cloudflare:
provider: cloudflare
apiToken: "your-cloudflare-api-token"
tlsProviders:
acme:
provider: acme
email: "admin@example.com"
docker:
local: # Name of the Docker target provider
host: unix:///var/run/docker.sock # Docker socket or daemon address
targetHostname: host.docker.internal # hostname or IP of docker server (ex: host.docker.internal or 172.31.0.1)
defaultProxyProvider: default # Default proxy provider for this Docker server
autoRestart: true # (optional) Enable automatic re-resolution on backend failure (default: true)
healthCheckEnabled: true # (optional) Enable health probes (default: true)
healthCheckInterval: 30 # (optional) Seconds between health probes (default: 30)
healthCheckFailures: 3 # (optional) Consecutive failures before re-resolution (default: 3)
healthCheckCooldown: 0 # (optional) Fixed cooldown in seconds, 0 for exponential backoff (default: 0)
lists:
critical: # Name of the target list provider
filename: /config/critical.yaml # Path to the proxy list file
defaultProxyProvider: tailscale1 # (Optional) Default proxy provider for this list
defaultProxyAccessLog: true # (Optional) Enable access logs for this list
autoRestart: true # (optional) Enable automatic re-resolution on backend failure (default: true)
healthCheckEnabled: true # (optional) Enable health probes (default: true)
healthCheckInterval: 30 # (optional) Seconds between health probes (default: 30)
healthCheckFailures: 3 # (optional) Consecutive failures before re-resolution (default: 3)
healthCheckCooldown: 0 # (optional) Fixed cooldown in seconds, 0 for exponential backoff (default: 0)
tailscale:
providers:
default: # Name of the Tailscale provider
clientId: "your_client_id" # OAuth client ID (generated by Tailscale)
clientSecret: "your_client_secret" # OAuth client secret (generated by Tailscale)
# If clientId and clientSecret are defined, authKey
# and authKeyFile are ignored
authKey: "" # Tailscale auth key (alternative to OAuth)
authKeyFile: "" # Path to a file containing the auth key (ignores authKey if defined)
tags: "tag:example,tag:server" # Default tags for all containers using this provider
# Container-specific tags override these default tags
controlUrl: https://controlplane.tailscale.com # Override the default Tailscale control URL
preventDuplicates: false # Delete stale tailnet devices before creating new nodes (OAuth only)
maxCertConcurrency: 2 # Max parallel TLS cert generation requests (default: 2)
dataDir: /data/ # Tailscale data directory
http:
hostname: 127.0.0.1 # HTTP server hostname (changed from 0.0.0.0 — see breaking changes)
port: 8080 # HTTP server port
log:
level: info # Logging level (debug, info, warn, error, fatal, panic, trace)
json: false # Enable JSON logging (true/false)
proxyAccessLog: true # Enable container access logs (true/false)
apiKey: "" # API key for non-Tailscale authentication (optional)
apiKeyFile: "" # Path to a file containing the API key (optional)
admins: [] # Tailscale UserProfile.IDs authorized for admin actions (optional)
# Example: admins: ["12345" # alice@github, "67890" # bob@example.com]
adminAllowLocalhost: false # Permit localhost to bypass admin allowlist (for bootstrapping)Configuration Sections
tailscale Section
Configures Tailscale integration.
dataDir
Specifies the data directory used by Tailscale. Defaults to /data/.
providers
Defines multiple Tailscale providers. Each provider has the following options:
default: # Provider name
authKey: your-authkey # Tailscale auth key
authKeyFile: "" # Path to auth key file
controlUrl: https://controlplane.tailscale.com # Tailscale control URL
shared: false # share one Tailscale connection for multiple proxies
hostname: "" # hostname for shared server (required when shared: true)| Field | Type | Default | Description |
|---|---|---|---|
shared | boolean | false | When true, all proxies using this provider share a single Tailscale connection (one tsnet.Server). Proxies are routed by SNI (TLS Server Name Indication). Requires a custom domain on every proxy. See Shared Tailscale. Mutually exclusive with services. |
services | boolean | false | When true, uses Tailscale VIP Services API. Each proxy gets an auto-assigned FQDN from Tailscale. Requires hostname, clientId, and clientSecret. No custom domain or UDP support. See Services Mode. Mutually exclusive with shared. |
hostname | string | "" | The Tailscale machine name. Required when shared: true or services: true. |
autoApproveDevices | boolean | false | Auto-approve device registration in Tailscale. Requires OAuth credentials. Useful in services mode where new nodes need approval before they can start. |
clientSecretFile | string | "" | Path to a file containing the OAuth client secret. Overrides clientSecret if both are set. |
Example with multiple providers:
tailscale:
providers:
default:
authKey: your-authkey
authKeyFile: ""
controlUrl: https://controlplane.tailscale.com
server1:
authKey: authkey-server1
authKeyFile: ""
controlUrl: http://server1
differentkey:
authKey: authkey-with-different-tags
authKeyFile: ""
controlUrl: https://controlplane.tailscale.comThis example configures three Tailscale providers: default (default server),
server1 (different Tailscale server), and differentkey (default server with
a different auth key for specific tags).
Example with a shared provider:
tailscale:
providers:
shared:
clientId: "your_client_id"
clientSecret: "your_client_secret"
tags: "tag:example"
shared: true
hostname: "shared-proxy"Example with a services/VIP provider:
tailscale:
providers:
services:
clientId: "your_client_id"
clientSecret: "your_client_secret"
tags: "tag:example"
services: true
hostname: "shared-services"preventDuplicates
Controls how TSDProxy handles stale Tailscale devices when the data directory
has been lost. A boolean option (default: false):
| Value | Behavior |
|---|---|
false | Do not check for duplicate devices (default) |
true | Check and remove offline duplicates before creating a new node (requires OAuth) |
TSDProxy logs a warning and disables preventDuplicates if it is set to true
without OAuth credentials (clientId + clientSecret).
tailscale:
providers:
default:
preventDuplicates: trueWarning
This deletes devices from your tailnet. See Prevent Duplicate Machines for safety checks and requirements.
reconcileInterval
Periodic interval for device reconciliation. TSDProxy checks for stale devices matching the provider’s hostname and tags, and removes offline ones.
Set to a duration string (e.g. "5m", "1h"). Default "0" disables periodic
reconciliation. Reconciliation also runs once on startup regardless of this
setting.
tailscale:
providers:
default:
reconcileInterval: "5m"authRetry
Configures the retry policy when tsnet startup fails (e.g. due to transient network issues or temporary Tailscale API errors).
tailscale:
providers:
default:
authRetry:
enabled: true
maxAttempts: 3
initialBackoff: "2s"
maxBackoff: "30s"| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Enable or disable startup retry |
maxAttempts | integer | 3 | Maximum retry attempts (1–10) |
initialBackoff | duration | "2s" | Initial backoff before first retry |
maxBackoff | duration | "30s" | Maximum backoff cap (exponential growth) |
Tip
Non-recoverable errors (e.g. invalid tags, expired auth keys) are detected
early and stop retrying regardless of maxAttempts.
Tip
For more details, see the Tailscale page.
docker Section
Configures Docker server connections. Multiple Docker servers can be defined:
local: # Docker provider name
host: unix:///var/run/docker.sock # Docker socket or daemon address
targetHostname: 172.31.0.1 # Docker server hostname or IP
defaultProxyProvider: default # Default proxy provider for this Docker serverExample with multiple Docker servers:
docker:
local:
host: unix:///var/run/docker.sock
defaultProxyProvider: default
srv1:
host: tcp://174.17.0.1:2376
targetHostname: 174.17.0.1
defaultProxyProvider: server1This example configures a local Docker server and a remote srv1 server.
host
Specifies the Docker socket or daemon address. Defaults to unix:///var/run/docker.sock.
targetHostname
Specifies the IP address or DNS name of the Docker server. Used for connecting to containers in specific cases.
defaultProxyProvider
Specifies the default Tailscale provider (defined in the tailscale.providers
section) to use for containers on this Docker server. Container-specific labels
override this setting.
tryDockerInternalNetwork
Defaults to false. When set to true, containers default to using
auto-detection of the target URL via connectivity probing. This sets the default
for the per-container tsdproxy.autodetect label. Individual containers can
still override this with the tsdproxy.autodetect label.
docker:
local:
host: unix:///var/run/docker.sock
targetHostname: host.docker.internal
defaultProxyProvider: default
tryDockerInternalNetwork: trueautoRestart
Defaults to true. When enabled, TSDProxy monitors each proxy’s backend health
and automatically re-resolves the target when consecutive health check failures
reach the configured threshold. See Health Check for details.
docker:
local:
host: unix:///var/run/docker.sock
autoRestart: true
healthCheckEnabled: true
healthCheckInterval: 30
healthCheckFailures: 3
healthCheckCooldown: 0healthCheckEnabled
Defaults to true. When set to false, health probes are completely disabled for
all containers using this provider. Individual containers can override this with
the tsdproxy.health_check_enabled Docker label. See Health Check for details.
healthCheckInterval
Seconds between health probes. Must be at least 1. Defaults to 30.
healthCheckFailures
Number of consecutive health check failures before triggering target re-resolution.
Must be at least 1. Defaults to 3.
healthCheckCooldown
Fixed cooldown in seconds between re-resolution attempts while the target remains
unhealthy. Set to 0 (default) to use exponential backoff instead.
dnsProviders Section
Configures DNS providers for custom domain support. Each provider manages CNAME records for a specific DNS zone. See Custom Domains for the full guide.
Cloudflare provider
dnsProviders:
cloudflare:
provider: cloudflare
apiToken: "your-cloudflare-api-token"| Field | Required | Description |
|---|---|---|
provider | yes | Must be cloudflare or magicdns |
apiToken | yes | Cloudflare API token with Zone:DNS:Edit and Zone:Zone:Read |
apiTokenFile | no | Path to a file containing the API token (overrides apiToken) |
defaultDNSProvider
The default DNS provider used when a container does not specify
tsdproxy.dnsprovider. Must match a provider name defined in dnsProviders.
defaultDNSProvider: cloudflaretlsProviders Section
Configures TLS providers for custom domain certificate provisioning.
Tailscale provider
Wraps Tailscale’s built-in CertPair for .ts.net domains. The config entry
name can be anything — TSDProxy resolves the provider type from the provider
field.
tlsProviders:
my-tailscale:
provider: tailscale| Field | Required | Description |
|---|---|---|
provider | yes | Must be tailscale |
ACME provider
Uses certmagic with DNS-01 challenge via the configured DNS provider.
tlsProviders:
acme:
provider: acme
email: "admin@example.com"| Field | Required | Default | Description |
|---|---|---|---|
provider | yes | - | Must be acme or tailscale |
email | yes | - | Email for ACME account registration |
ca | no | Let’s Encrypt Production | ACME directory URL |
certStorage | no | data directory | Path to store certificates |
defaultTLSProvider
The default TLS provider used when a container does not specify
tsdproxy.tlsprovider. Must match a provider name defined in tlsProviders,
or be a tailscale-type provider (resolved by provider field).
defaultTLSProvider: acmecleanupDNS
When set to true, TSDProxy removes DNS CNAME records when a proxy stops.
Defaults to true.
cleanupDNS: truehttp Section
Configures the built-in HTTP server that serves the dashboard and health endpoints.
hostname
The bind address for the HTTP server. Defaults to 127.0.0.1 (localhost only).
When running inside Docker, the hostname is automatically overridden to 0.0.0.0
so that port-mapped access (-p 8080:8080) works without manual configuration.
Warning
The default changed from 0.0.0.0 to 127.0.0.1 in v2.2.0 for security.
If you need the dashboard accessible on all interfaces outside Docker,
set hostname: 0.0.0.0 explicitly.
port
The port for the HTTP server. Defaults to 8080.
log Section
level
Defines the logging level. Options are debug, info, warn, error, fatal, panic, or trace.
The default is info.
json
Enables JSON-formatted logging when set to true. Defaults to false.
proxyAccessLog
Enables access logging for proxied requests. Defaults to true. Can be
overridden per-container with the tsdproxy.containeraccesslog label or
per-list with defaultProxyAccessLog.
API Key Authentication
In addition to Tailscale identity, TSDProxy supports API key authentication for non-Tailscale clients (scripts, CI pipelines, monitoring tools).
apiKey: "your-secret-api-key"Or via a file:
apiKeyFile: "/run/secrets/tsdproxy-api-key"When configured, include the key in API requests:
curl -H "Authorization: Bearer your-secret-api-key" http://localhost:8080/api/v1/proxiesNote
API keys grant full admin access to all endpoints. Treat them like passwords —
store in secrets managers, use apiKeyFile with Docker secrets, and rotate regularly.
apiKey
A static API key for authenticating non-Tailscale clients. If both apiKey and
apiKeyFile are set, apiKeyFile takes precedence.
apiKeyFile
Path to a file containing the API key. The file is read at startup and must contain exactly one line with the key.
telemetry Section
Configures OpenTelemetry export for distributed tracing. Disabled by default.
telemetry:
enabled: false
endpoint: "localhost:4317"
insecure: falseenabled
Enable or disable OpenTelemetry tracing export. Defaults to false.
endpoint
The OTLP gRPC endpoint to send traces to. Defaults to localhost:4317.
insecure
When true, connects to the telemetry endpoint without TLS. Defaults to false.
webhooks Section
Configures webhook notifications for proxy status changes. See Notifications for detailed recipes.
webhooks:
- url: "https://ntfy.sh/my-topic" # Target URL (required)
type: ntfy # ntfy | discord | slack | generic (default)
events: # Optional — send all events if omitted
- Running
- Stopped
- Error
headers: # Optional custom HTTP headers
Authorization: "Bearer token123"Multiple webhooks can be configured simultaneously:
webhooks:
- url: "https://ntfy.sh/alerts"
type: ntfy
events: [Running, Error, Stopped]
- url: "https://discord.com/api/webhooks/123/abc"
type: discord
events: [Error]url
The HTTP endpoint to send notifications to. Required.
type
The message format. Supported values:
| Type | Format | Content-Type |
|---|---|---|
ntfy | Plain text body | text/plain |
discord | Discord embed JSON | application/json |
slack | Slack Block Kit JSON | application/json |
generic (default) | JSON payload | application/json |
events
Optional list of status names that trigger a notification. Names are case-insensitive. Available events: Initializing, Starting, Authenticating, Running, Stopping, Stopped, Error, Paused. If omitted, all status changes are sent.
headers
Optional map of HTTP headers added to every request. Custom headers override defaults (including Content-Type).
Admin Allowlist
Controls access to sensitive dashboard actions (restart, pause, resume, reauth).
admins:
- "12345" # alice@github
- "67890" # bob@example.com
adminAllowLocalhost: falseadmins
A list of Tailscale UserProfile.ID values authorized to use admin endpoints.
All tailnet users can view proxy status and preferences (viewer role). Only
users in this list (or authenticated via API key) can perform admin actions.
Use /api/whoami through a Tailscale connection to discover your ID.
adminAllowLocalhost
When true, requests originating from loopback (127.0.0.0/8, ::1) or
RFC 1918 private networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
bypass the admin allowlist. Defaults to false. Intended only for
bootstrapping — enable temporarily, then disable once your ID is in the list.
The private-network check enables adminAllowLocalhost to work with Docker
port mapping, where requests arrive from the Docker bridge gateway
(e.g. 172.17.0.1) rather than 127.0.0.1.
Warning
With adminAllowLocalhost: true, any process on the host or Docker
network can call admin endpoints without authentication.
Configuration File Lifecycle
Auto-Generation
On first run, TSDProxy generates a default config automatically.
Live Reload
- Proxy list files reload automatically on changes. No restart needed.
- Main config changes require a restart.
Validation
Config files are strictly validated. Unknown keys or invalid values cause load failures.