Get tsbridge running in 5 minutes.
- Go to https://login.tailscale.com/admin/settings/oauth
- Click Generate OAuth client...
- Name it something like
tsbridge - Under Scopes → Auth Keys, check both Read and Write
- Tags: Select a tag (e.g.,
tag:server) - this tag will be used by your services - Click Generate client
- Save the Client ID and Secret
Create tsbridge.toml:
[tailscale]
oauth_client_id_env = "TS_OAUTH_CLIENT_ID"
oauth_client_secret_env = "TS_OAUTH_CLIENT_SECRET"
default_tags = ["tag:server"] # Must match or be owned by your OAuth client's tag
[[services]]
name = "app"
backend_addr = "localhost:8080"export TS_OAUTH_CLIENT_ID=your-client-id
export TS_OAUTH_CLIENT_SECRET=your-client-secret
tsbridge -config tsbridge.tomlYour service is now available at https://app.<tailnet>.ts.net
[[services]]
name = "api"
backend_addr = "localhost:8080"
[[services]]
name = "web"
backend_addr = "localhost:3000"[[services]]
name = "app"
backend_addr = "unix:///var/run/app.sock"[[services]]
name = "internal-api"
backend_addr = "localhost:8080"
whois_enabled = true # Adds X-Tailscale-User headers[[services]]
name = "events"
backend_addr = "localhost:3000"
write_timeout = "0s" # No timeout
flush_interval = "-1ms" # No buffering[tailscale]
oauth_client_id_file = "/etc/tsbridge/oauth-id"
oauth_client_secret_file = "/etc/tsbridge/oauth-secret"
state_dir = "/var/lib/tsbridge"
default_tags = ["tag:server", "tag:prod"]
[global]
metrics_addr = ":9090" # Prometheus metrics
[[services]]
name = "api"
backend_addr = "api.internal:8080"
whois_enabled = true
downstream_headers = {
"Strict-Transport-Security" = "max-age=63072000"
}services:
tsbridge:
image: ghcr.io/jtdowney/tsbridge:latest
command: ["--provider", "docker"]
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- tsbridge-state:/var/lib/tsbridge
environment:
- TS_OAUTH_CLIENT_ID=${TS_OAUTH_CLIENT_ID}
- TS_OAUTH_CLIENT_SECRET=${TS_OAUTH_CLIENT_SECRET}
labels:
- "tsbridge.tailscale.oauth_client_id_env=TS_OAUTH_CLIENT_ID"
- "tsbridge.tailscale.oauth_client_secret_env=TS_OAUTH_CLIENT_SECRET"
- "tsbridge.tailscale.state_dir=/var/lib/tsbridge"
- "tsbridge.tailscale.default_tags=tag:server" # Must match or be owned by your OAuth client's tag
myapp:
image: myapp:latest
labels:
- "tsbridge.enabled=true"
- "tsbridge.service.name=myapp"
- "tsbridge.service.port=8080"
# Optional: Override default tags for this service
# - "tsbridge.service.tags=tag:api,tag:prod"
volumes:
tsbridge-state:Network Requirements: tsbridge and service containers must be on the same Docker network. They don't need to be in the same compose file, but network connectivity is required. See Docker Labels - Docker Networking for multi-compose setups.
tsbridge -config tsbridge.toml -validate- "services must have at least one tag": Add
default_tagsto[tailscale]section - "OAuth client ID not set": Check your environment variables
- Connection timeouts: For streaming, set
write_timeout = "0s" - Tag authorization errors: Ensure tags match or are owned by your OAuth client's tag. See Tag Ownership and OAuth Security
- See Configuration Reference for all options
- Check Docker Labels for dynamic container management
- Review examples/ for complete setups