Skip to content
Server configuration

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.yaml

Important

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.

/config/tsdproxy.yaml
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:

/config/tsdproxy.yaml
  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)
FieldTypeDefaultDescription
sharedbooleanfalseWhen 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.
servicesbooleanfalseWhen 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.
hostnamestring""The Tailscale machine name. Required when shared: true or services: true.
autoApproveDevicesbooleanfalseAuto-approve device registration in Tailscale. Requires OAuth credentials. Useful in services mode where new nodes need approval before they can start.
clientSecretFilestring""Path to a file containing the OAuth client secret. Overrides clientSecret if both are set.

Example with multiple providers:

/config/tsdproxy.yaml
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.com

This 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:

/config/tsdproxy.yaml
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:

/config/tsdproxy.yaml
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):

ValueBehavior
falseDo not check for duplicate devices (default)
trueCheck 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).

/config/tsdproxy.yaml
tailscale:
  providers:
    default:
      preventDuplicates: true

Warning

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.

/config/tsdproxy.yaml
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).

/config/tsdproxy.yaml
tailscale:
  providers:
    default:
      authRetry:
        enabled: true
        maxAttempts: 3
        initialBackoff: "2s"
        maxBackoff: "30s"
FieldTypeDefaultDescription
enabledbooleantrueEnable or disable startup retry
maxAttemptsinteger3Maximum retry attempts (1–10)
initialBackoffduration"2s"Initial backoff before first retry
maxBackoffduration"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:

/config/tsdproxy.yaml
  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 server

Example with multiple Docker servers:

/config/tsdproxy.yaml
docker:
  local:
    host: unix:///var/run/docker.sock
    defaultProxyProvider: default
  srv1:
    host: tcp://174.17.0.1:2376
    targetHostname: 174.17.0.1
    defaultProxyProvider: server1

This 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.

/config/tsdproxy.yaml
docker:
  local:
    host: unix:///var/run/docker.sock
    targetHostname: host.docker.internal
    defaultProxyProvider: default
    tryDockerInternalNetwork: true
autoRestart

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.

/config/tsdproxy.yaml
docker:
  local:
    host: unix:///var/run/docker.sock
    autoRestart: true
    healthCheckEnabled: true
    healthCheckInterval: 30
    healthCheckFailures: 3
    healthCheckCooldown: 0
healthCheckEnabled

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
/config/tsdproxy.yaml
dnsProviders:
  cloudflare:
    provider: cloudflare
    apiToken: "your-cloudflare-api-token"
FieldRequiredDescription
provideryesMust be cloudflare or magicdns
apiTokenyesCloudflare API token with Zone:DNS:Edit and Zone:Zone:Read
apiTokenFilenoPath 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.

/config/tsdproxy.yaml
defaultDNSProvider: cloudflare

tlsProviders 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.

/config/tsdproxy.yaml
tlsProviders:
  my-tailscale:
    provider: tailscale
FieldRequiredDescription
provideryesMust be tailscale
ACME provider

Uses certmagic with DNS-01 challenge via the configured DNS provider.

/config/tsdproxy.yaml
tlsProviders:
  acme:
    provider: acme
    email: "admin@example.com"
FieldRequiredDefaultDescription
provideryes-Must be acme or tailscale
emailyes-Email for ACME account registration
canoLet’s Encrypt ProductionACME directory URL
certStoragenodata directoryPath 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).

/config/tsdproxy.yaml
defaultTLSProvider: acme

cleanupDNS

When set to true, TSDProxy removes DNS CNAME records when a proxy stops. Defaults to true.

/config/tsdproxy.yaml
cleanupDNS: true

http 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).

/config/tsdproxy.yaml
apiKey: "your-secret-api-key"

Or via a file:

/config/tsdproxy.yaml
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/proxies

Note

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.

/config/tsdproxy.yaml
telemetry:
  enabled: false
  endpoint: "localhost:4317"
  insecure: false
enabled

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.

/config/tsdproxy.yaml
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:

/config/tsdproxy.yaml
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:

TypeFormatContent-Type
ntfyPlain text bodytext/plain
discordDiscord embed JSONapplication/json
slackSlack Block Kit JSONapplication/json
generic (default)JSON payloadapplication/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).

/config/tsdproxy.yaml
admins:
  - "12345"  # alice@github
  - "67890"  # bob@example.com
adminAllowLocalhost: false
admins

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.

Last updated on