Skip to main content

Overview

Tailscale containers in ScaleTail are configured through environment variables. This reference documents all available variables, their types, defaults, and usage.
All environment variables prefixed with TS_ are Tailscale-specific and control the behavior of the Tailscale sidecar container.

Core Variables

These variables are required for basic Tailscale functionality.

TS_AUTHKEY

TS_AUTHKEY
string
required
Authentication key for joining your Tailscale network (Tailnet)
Description: The auth key allows your Docker container to automatically authenticate with your Tailnet. Generate this key from the Tailscale Admin Console. Usage:
environment:
  - TS_AUTHKEY=${TS_AUTHKEY}
Example:
.env
TS_AUTHKEY=tskey-auth-xxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Never commit auth keys to version control. Always use environment variables or secrets management.
Best Practices:
  • Use reusable keys for multiple service deployments
  • Enable pre-approved to skip manual device authorization
  • Set appropriate expiration periods (90 days is common)
  • Use tagged keys to automatically apply ACL rules

TS_STATE_DIR

TS_STATE_DIR
string
default:"/var/lib/tailscale"
Directory where Tailscale stores state information
Description: This directory persists your Tailscale connection state, keys, and configuration across container restarts. Must be mounted to a volume for persistence. Usage:
environment:
  - TS_STATE_DIR=/var/lib/tailscale
volumes:
  - ./ts/state:/var/lib/tailscale
Always mount this directory to a host volume. Without persistence, your container will create a new Tailscale identity on every restart.
Directory Contents:
  • tailscaled.state - Connection state and machine identity
  • Private keys and certificates
  • Local configuration

Serve and Funnel Configuration

TS_SERVE_CONFIG

TS_SERVE_CONFIG
string
Path to the serve.json configuration file for Tailscale Serve/Funnel
Description: Defines how Tailscale should route incoming requests to your application. This enables Tailscale Serve (private) or Funnel (public) access. Usage:
environment:
  - TS_SERVE_CONFIG=/config/serve.json
configs:
  - source: ts-serve
    target: /config/serve.json
Example Configuration:
serve.json
{
  "TCP": {
    "443": {"HTTPS": true}
  },
  "Web": {
    "jellyfin.your-tailnet.ts.net:443": {
      "Handlers": {
        "/": {
          "Proxy": "http://127.0.0.1:8096"
        }
      }
    }
  },
  "AllowFunnel": {
    "jellyfin.your-tailnet.ts.net:443": false
  }
}
Configuration Fields:
"TCP": {
  "443": {"HTTPS": true}
}
Enables HTTPS on port 443. Tailscale automatically provisions TLS certificates.
If TS_SERVE_CONFIG is not set, your service will join the Tailnet but won’t be accessible via Tailscale Serve/Funnel. You’ll need to connect directly via IP address.
Common Ports:
  • Jellyfin: 8096
  • Portainer: 9000
  • Vaultwarden: 80
  • Home Assistant: 8123
  • Plex: 32400

TS_CERT_DOMAIN

TS_CERT_DOMAIN
string
Domain name for TLS certificate (used in serve.json template)
Description: While not a Tailscale environment variable itself, this is commonly used in ScaleTail configurations to template the serve.json file. Usage:
configs:
  ts-serve:
    content: |
      {"TCP":{"443":{"HTTPS":true}},
      "Web":{"${TS_CERT_DOMAIN}:443":
          {"Handlers":{"/":
          {"Proxy":"http://127.0.0.1:8096"}}}},
      "AllowFunnel":{"${TS_CERT_DOMAIN}:443":false}}
Example:
.env
TS_CERT_DOMAIN=jellyfin.your-tailnet.ts.net
The ${TS_CERT_DOMAIN} syntax is Docker Compose variable substitution, not a Tailscale feature. The $$ escapes the $ so Docker Compose doesn’t try to substitute it.

Network Configuration

TS_USERSPACE

TS_USERSPACE
boolean
default:"true"
Run Tailscale in userspace networking mode
Description: Controls whether Tailscale uses kernel networking or userspace networking. Usage:
environment:
  - TS_USERSPACE=false
Values:
  • true: Userspace networking (default) - More portable, works without elevated privileges
  • false: Kernel networking - Better performance, requires cap_add: net_admin and /dev/net/tun
ScaleTail Default: false Why? ScaleTail uses kernel networking for better performance and full feature support. Required for Kernel Mode (TS_USERSPACE=false):
devices:
  - /dev/net/tun:/dev/net/tun
cap_add:
  - net_admin

TS_ACCEPT_DNS

TS_ACCEPT_DNS
boolean
default:"false"
Accept DNS configuration from Tailscale (MagicDNS)
Description: When enabled, the container will use Tailscale’s MagicDNS for name resolution, allowing you to access other devices by their Tailscale hostnames. Usage:
environment:
  - TS_ACCEPT_DNS=true
When to Enable:
  • You have MagicDNS enabled in your Tailnet
  • Your application needs to resolve Tailscale hostnames
  • You want automatic DNS for other Tailnet devices
Default in ScaleTail: Commented out (disabled)
# Uncomment when using MagicDNS
#- TS_ACCEPT_DNS=true
Enable MagicDNS in your Tailnet at https://login.tailscale.com/admin/dns

TS_EXTRA_ARGS

TS_EXTRA_ARGS
string
Additional command-line arguments to pass to tailscaled
Description: Allows passing arbitrary flags to the Tailscale daemon for advanced configurations. Usage:
environment:
  - TS_EXTRA_ARGS=--advertise-exit-node
Common Use Cases:
environment:
  - TS_EXTRA_ARGS=--advertise-exit-node
Advertises this device as an exit node, allowing other devices on your Tailnet to route all internet traffic through it.Additional Requirements:
sysctls:
  net.ipv4.ip_forward: 1
  net.ipv6.conf.all.forwarding: 1
Full Flag Reference: Tailscale CLI documentation

Authentication and Security

TS_AUTH_ONCE

TS_AUTH_ONCE
boolean
default:"false"
Only authenticate on first run, reuse existing state on subsequent runs
Description: When enabled, the container will only use the auth key on the first startup. Subsequent restarts will reuse the existing machine identity. Usage:
environment:
  - TS_AUTH_ONCE=true
Benefits:
  • Prevents consuming auth key usage limits on restarts
  • Maintains stable machine identity
  • Recommended for production deployments
ScaleTail Default: true
If you delete the state directory (TS_STATE_DIR) with TS_AUTH_ONCE=true, the container won’t be able to re-authenticate. You’ll need to temporarily set it to false or provide a new auth key.

Health and Monitoring

TS_ENABLE_HEALTH_CHECK

TS_ENABLE_HEALTH_CHECK
boolean
default:"false"
Enable the built-in HTTP health check endpoint
Description: Enables a health check endpoint at /healthz that returns the Tailscale connection status. Usage:
environment:
  - TS_ENABLE_HEALTH_CHECK=true
  - TS_LOCAL_ADDR_PORT=127.0.0.1:41234
ScaleTail Default: true Why? Health checks ensure the application container only starts after Tailscale has successfully connected. Docker Health Check:
healthcheck:
  test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"]
  interval: 1m
  timeout: 10s
  retries: 3
  start_period: 10s
Health Check Response:
  • 200 OK: Tailscale connected and operational
  • 503 Service Unavailable: Tailscale not connected or starting up

TS_LOCAL_ADDR_PORT

TS_LOCAL_ADDR_PORT
string
default:":41112"
Address and port for the local API and health check endpoint
Description: Specifies where the Tailscale local API and health check endpoint listen. Usage:
environment:
  - TS_LOCAL_ADDR_PORT=127.0.0.1:41234
Format: <address>:<port>
  • 127.0.0.1:41234 - Only localhost (recommended)
  • :41234 - All interfaces
  • 0.0.0.0:41234 - All interfaces (explicit)
ScaleTail Default: 127.0.0.1:41234
The port must not conflict with your application’s port. The default 41234 is chosen to avoid common application ports.

Complete Example

Here’s a complete example showing all commonly used environment variables:
configs:
  ts-serve:
    content: |
      {"TCP":{"443":{"HTTPS":true}},
      "Web":{"${TS_CERT_DOMAIN}:443":
          {"Handlers":{"/":
          {"Proxy":"http://127.0.0.1:8096"}}}},
      "AllowFunnel":{"${TS_CERT_DOMAIN}:443":false}}

services:
  tailscale:
    image: tailscale/tailscale:latest
    container_name: tailscale-jellyfin
    hostname: jellyfin
    environment:
      # Core Configuration
      - TS_AUTHKEY=${TS_AUTHKEY}
      - TS_STATE_DIR=/var/lib/tailscale
      
      # Serve/Funnel
      - TS_SERVE_CONFIG=/config/serve.json
      
      # Network
      - TS_USERSPACE=false
      - TS_ACCEPT_DNS=true  # Enable if using MagicDNS
      
      # Authentication
      - TS_AUTH_ONCE=true
      
      # Health Check
      - TS_ENABLE_HEALTH_CHECK=true
      - TS_LOCAL_ADDR_PORT=127.0.0.1:41234
    
    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
    
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"]
      interval: 1m
      timeout: 10s
      retries: 3
      start_period: 10s
    
    restart: always
  
  application:
    image: jellyfin/jellyfin:latest
    network_mode: service:tailscale
    container_name: app-jellyfin
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/Amsterdam
    volumes:
      - ./jellyfin-data/config:/config
      - ./media:/media
    depends_on:
      tailscale:
        condition: service_healthy
    restart: always

Environment Variable Reference Table

VariableTypeRequiredDefaultDescription
TS_AUTHKEYstringYes-Authentication key for Tailnet
TS_STATE_DIRstringNo/var/lib/tailscaleState persistence directory
TS_SERVE_CONFIGstringNo-Path to serve.json configuration
TS_USERSPACEbooleanNotrueUse userspace networking
TS_ACCEPT_DNSbooleanNofalseAccept MagicDNS configuration
TS_EXTRA_ARGSstringNo-Additional tailscaled arguments
TS_AUTH_ONCEbooleanNofalseOnly authenticate on first run
TS_ENABLE_HEALTH_CHECKbooleanNofalseEnable health check endpoint
TS_LOCAL_ADDR_PORTstringNo:41112Health check endpoint address

Service-Specific Variables

These are not Tailscale variables but are commonly used in ScaleTail configurations:

SERVICE

SERVICE
string
required
Name of the service being deployed
Usage:
container_name: tailscale-${SERVICE}
hostname: ${SERVICE}
Example:
.env
SERVICE=jellyfin

IMAGE_URL

IMAGE_URL
string
required
Docker image for the application container
Usage:
application:
  image: ${IMAGE_URL}
Example:
.env
IMAGE_URL=jellyfin/jellyfin:latest

DNS_SERVER

DNS_SERVER
string
Custom DNS server for the Tailscale container
Usage:
tailscale:
  dns:
    - ${DNS_SERVER}
Example:
.env
DNS_SERVER=1.1.1.1

Exit Node Example

For deploying a Tailscale exit node, use this configuration:
compose.yaml
services:
  tailscale:
    image: tailscale/tailscale:latest
    container_name: tailscale-exit-node
    hostname: exit-node
    environment:
      - TS_AUTHKEY=${TS_AUTHKEY}
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_EXTRA_ARGS=--advertise-exit-node
      - TS_USERSPACE=false
      - TS_ENABLE_HEALTH_CHECK=true
      - TS_LOCAL_ADDR_PORT=127.0.0.1:41234
      - TS_AUTH_ONCE=true
    volumes:
      - ./ts/state:/var/lib/tailscale
    devices:
      - /dev/net/tun:/dev/net/tun
    dns:
      - ${DNS_SERVER}
    sysctls:
      net.ipv4.ip_forward: 1
      net.ipv6.conf.all.forwarding: 1
    cap_add:
      - net_admin
    network_mode: bridge
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"]
      interval: 1m
      timeout: 10s
      retries: 3
      start_period: 10s
    restart: always
Exit nodes require sysctls for IP forwarding and typically use network_mode: bridge instead of sharing with an application container.

Troubleshooting

Symptoms: Container fails to join Tailnet, logs show auth errorsSolutions:
  • Verify the key is correct and complete
  • Check if the key has expired
  • Ensure the key hasn’t reached its usage limit (for non-reusable keys)
  • Try generating a new key
Symptoms: New machine appears in Tailnet on every restartSolutions:
  • Verify TS_STATE_DIR volume is correctly mounted
  • Check directory permissions (should be writable by container)
  • Ensure TS_AUTH_ONCE=true is set
  • Check if the volume path exists on host
Symptoms: Container shows unhealthy, application doesn’t startSolutions:
  • Verify TS_ENABLE_HEALTH_CHECK=true is set
  • Check TS_LOCAL_ADDR_PORT matches health check URL
  • Ensure port is not in use by application
  • Review Tailscale logs for connection issues
  • Test manually: docker exec tailscale-service wget -O- http://127.0.0.1:41234/healthz
Symptoms: Can’t resolve Tailscale hostnames from containerSolutions:
  • Enable TS_ACCEPT_DNS=true
  • Verify MagicDNS is enabled in Tailnet settings
  • Check DNS configuration in Tailscale admin console
  • Test resolution: docker exec app-service nslookup other-device.tailnet.ts.net
Symptoms: Container fails with network errors when TS_USERSPACE=falseSolutions:
  • Verify /dev/net/tun exists: ls -l /dev/net/tun
  • Ensure cap_add: - net_admin is present
  • Check container has necessary permissions
  • Try userspace mode as fallback: TS_USERSPACE=true

Best Practices

Use .env Files

Store all variables in .env files and add them to .gitignore to prevent committing secrets.

Enable Health Checks

Always use TS_ENABLE_HEALTH_CHECK=true to ensure reliable startup ordering with application containers.

Persist State

Always mount TS_STATE_DIR to a volume to maintain stable machine identity across restarts.

Use TS_AUTH_ONCE

Set TS_AUTH_ONCE=true in production to avoid consuming auth key limits on restarts.

Kernel Mode Performance

Use TS_USERSPACE=false with proper capabilities for better performance in production.

Document Overrides

Comment why you’re using non-default values, especially for TS_EXTRA_ARGS.

Next Steps

Tailscale Setup

Generate auth keys and configure your Tailnet

Sidecar Pattern

Understand how these variables work in the sidecar architecture

Serve vs Funnel

Learn how TS_SERVE_CONFIG controls access

Deploy Services

Put these variables into practice

Build docs developers (and LLMs) love