Configuration

A single bridge.toml (or YAML) file describes the node, the proxy, and the fleet.

Config discovery

The daemon looks for its configuration in this order:

  1. ./bridge.toml: the current working directory
  2. $BRIDGE_CONFIG: path from the environment variable
  3. /etc/bridge/bridge.toml: the system location the installer uses

You can always override discovery with bridge --config /path/to/bridge.toml. Both TOML and YAML are accepted. Omitting the [node] section runs the daemon in standalone mode (no mesh, no election).

Full reference

# ==============================================================================
# BRIDGE CONFIGURATION REFERENCE (bridge.toml)
# ==============================================================================

# Global telemetry reporting (anonymized error metrics). Disabled by default.
enable_telemetry = false

# ------------------------------------------------------------------------------
# Logger Configuration
# ------------------------------------------------------------------------------
[logger]
level = "INFO"        # TRACE | DEBUG | INFO | WARN | ERROR
format = "text"       # "text" for human-readable output, "json" for structured logs
target = "stderr"     # "stderr" or "stdout"
ansi = true           # ANSI color output in console logs

# ------------------------------------------------------------------------------
# Ingress Proxy Settings
# ------------------------------------------------------------------------------
[proxy]
# Operating Modes:
#   "Managed" - Recommended for Coolify/Traefik. Bridge manages dynamic routes.
#   "Direct"  - L7 reverse proxy. Bridge binds ports 80/443 and terminates TLS.
#   "Handoff" - L4 transparent passthrough with zero-copy TCP splicing & PROXY v2.
mode = "Managed"

# Target routing transport: "Mesh" (WireGuard overlay 10.8.0.x) or "Direct" (public IP)
routing = "Mesh"

# Maximum concurrency worker task ceiling (number or "auto" based on CPU cores)
max_concurrency = "auto"

# Listeners and bind addresses (used when mode = "Direct" or "Handoff")
listeners = ["http", "https"]
http_addr = "0.0.0.0:80"
https_addr = "0.0.0.0:443"
redirect_http = false      # Redirect port 80 traffic to port 443

# ------------------------------------------------------------------------------
# Managed Mode Settings (Coolify / Traefik Dynamic Provider)
# ------------------------------------------------------------------------------
[proxy.managed]
# Path to Traefik dynamic file (auto-detects /data/coolify/proxy/dynamic/bridge.yaml)
# dynamic_config_path = "/data/coolify/proxy/dynamic/bridge.yaml"

local_node_id = "node-01"            # Local routes skipped to let Traefik hit local containers directly
entrypoints = ["websecure"]          # Traefik entrypoints to bind routers to
tls_enabled = true                   # Generate TLS router rules in Traefik
cert_resolver = "letsencrypt"        # Traefik certificate resolver name
default_target_scheme = "http"       # Target scheme for cross-node upstream servers
sync_interval = "5s"                 # Interval for syncing domain registry to Traefik
dashboard_domain = "bridge-dash.example.com"  # Optional public dashboard domain
dashboard_upstream = "http://172.17.0.1:9090" # Host URL reachable from Traefik container

# ------------------------------------------------------------------------------
# Embedded Web Dashboard & Authentication
# ------------------------------------------------------------------------------
[dashboard]
enabled = true
listen_addr = "0.0.0.0:9090"         # Bind address (use 127.0.0.1:9090 for loopback only)
public_domain = "bridge-dash.example.com" # Optional leader-only public domain

[auth]
enabled = true                       # Enforce authentication for the dashboard

# First-Run Setup Wizard: when enabled = true and [[auth.users]] is omitted,
# the web UI at /login automatically presents an initial admin setup wizard.
# Alternatively, pre-configure static users with Argon2 hashes:
# [[auth.users]]
# username = "admin"
# password_hash = "$argon2id$v=19$m=65536,t=3,p=4$..."

# ------------------------------------------------------------------------------
# Unix Domain Socket IPC (Local CLI Control)
# ------------------------------------------------------------------------------
[ipc]
enabled = true
socket_path = "/tmp/bridge.sock"

# ------------------------------------------------------------------------------
# Docker Container Auto-Discovery
# ------------------------------------------------------------------------------
[discovery]
enabled = true
docker_socket = "/var/run/docker.sock"
default_node_id = "node-01"

# ------------------------------------------------------------------------------
# Cluster Identity & WireGuard Mesh Configuration
# ------------------------------------------------------------------------------
# (Omit the [node] block for standalone single-server mode)
[node]
id = "node-01"
endpoint = "203.0.113.10:51820"     # Public endpoint peers dial
listen_port = 51820                 # WireGuard listen port
mesh_ip = "10.8.0.1"                # Address on the 10.8.0.0/24 overlay mesh
priority = 100                      # Bully election priority (highest wins leadership)

# ------------------------------------------------------------------------------
# Bootstrap Cluster Seeds
# ------------------------------------------------------------------------------
[[seeds]]
id = "node-01"
endpoint = "203.0.113.10:51820"
public_key = "BASE64_PUBLIC_KEY"
mesh_ip = "10.8.0.1"

# ------------------------------------------------------------------------------
# Known Fleet Nodes & Target Backends
# ------------------------------------------------------------------------------
[[nodes]]
id = "node-01"
endpoint = "203.0.113.10:80"
mesh_endpoint = "10.8.0.1:80"

[[nodes]]
id = "node-02"
endpoint = "203.0.113.11:80"
mesh_endpoint = "10.8.0.2:80"

# ------------------------------------------------------------------------------
# Static Domain Services & Application Routing
# ------------------------------------------------------------------------------
[[services]]
url = "https://app.example.com"
node_id = "node-01"
upstream = "127.0.0.1:3000"

# Workload Failover Duplication
[services.replicate]
enabled = true
image = "myorg/api:v1.2.0"
env = ["PORT=3000", "NODE_ENV=production"]
container_port = 3000
placement = "leader"                # "ring" | "leader" | explicit node_id
failback_mode = "preemptive"        # "preemptive" | "non_preemptive" | "manual"
failback_cooldown_secs = 10         # Flapping prevention cooldown

# ------------------------------------------------------------------------------
# Ingress Handoff & Failover (Tiers 2 & 3)
# ------------------------------------------------------------------------------
[handoff]
mode = "none"                       # "none" | "dns" | "tunnel" | "floating_ip"

# [handoff.tunnel]
# token = "env:CF_TUNNEL_TOKEN"
# warm_standby = false

# [handoff.dns]
# provider = "cloudflare"
# zone_id = "YOUR_ZONE_ID"
# record_name = "app.example.com"
# api_token = "env:CF_API_TOKEN"

# ------------------------------------------------------------------------------
# Optional Sentry Error Monitoring
# ------------------------------------------------------------------------------
[sentry]
# dsn = "env:SENTRY_DSN"
environment = "production"
sample_rate = 1.0

Sections

Top level

KeyTypeDescription
enable_telemetrybooleanOpt-in telemetry. Disabled by default.

[proxy]

KeyTypeDescription
modestringManaged (dynamically configures Traefik / Coolify), Direct (L7 HTTP reverse proxy with TLS), or Handoff (L4 transparent passthrough with zero-copy TCP splice).
routingstringTarget transport preference: Mesh (WireGuard overlay 10.8.0.x) or Direct (public IP). Default Mesh.
max_concurrencynumber | stringWorker concurrency ceiling (number or "auto"). Default 20.
listenersarrayListener kinds to enable in Direct/Handoff modes, e.g. ["http", "https"].
http_addrstringBind address for HTTP ingress, e.g. 0.0.0.0:80.
https_addrstringBind address for HTTPS ingress, e.g. 0.0.0.0:443.
redirect_httpbooleanRedirect HTTP to HTTPS when both listeners are active.

[proxy.managed]

Settings for Mode 3: Managed Mode (Coolify / Traefik dynamic configuration provider).

KeyTypeDescription
local_node_idstringIdentifier of this node; local container routes are skipped so Traefik contacts them directly.
dynamic_config_pathstringPath to Traefik dynamic YAML file (auto-detects /data/coolify/proxy/dynamic/bridge.yaml).
entrypointsarrayTraefik entrypoints to bind routers to, e.g. ["websecure"].
tls_enabledbooleanGenerate TLS router configuration in Traefik. Default true.
cert_resolverstringTraefik certificate resolver name, e.g. "letsencrypt".
default_target_schemestringTarget scheme for cross-node upstream servers ("http" or "https").
sync_intervalstringInterval for synchronizing registry updates into Traefik's dynamic file, e.g. "5s".
dashboard_domainstringOptional public domain to expose the leader's dashboard through Traefik.
dashboard_upstreamstringExplicit server URL for Traefik to reach the dashboard container/host, e.g. "http://172.17.0.1:9090".

[dashboard]

KeyTypeDescription
enabledbooleanServe the embedded operations dashboard. Default true.
listen_addrstringBind address, e.g. 0.0.0.0:9090 or 127.0.0.1:9090 (loopback only).
public_domainstringOptional public domain routed exclusively to the currently elected cluster leader.

[auth]

Authentication settings for the embedded dashboard.

KeyTypeDescription
enabledbooleanEnforce authentication for dashboard endpoints and UI. Default false.
usersarrayArray of user tables with username and Argon2 PHC password_hash. When enabled = true and users is omitted or empty, the First-Run Web Setup Wizard automatically launches at /login to initialize the admin credentials.

[discovery]

KeyTypeDescription
enabledbooleanWatch Docker for labeled containers and turn them into routes automatically.
docker_socketstringPath to the Docker socket, e.g. /var/run/docker.sock.
default_node_idstringNode id assigned to discovered routes on this host.

[ipc]

KeyTypeDescription
enabledbooleanServe the IPC socket the bridge CLI talks to.
socket_pathstringDefault /tmp/bridge.sock.

[logger]

KeyTypeDescription
levelstringLog level, e.g. INFO.
formatstringOutput format, e.g. text.

[node]

Cluster identity for this node. Omit the whole section for standalone mode.

KeyTypeDescription
idstringUnique node identifier, e.g. node-01.
mesh_ipstringThis node's address on the WireGuard overlay, e.g. 10.8.0.1.
endpointstringPublic address peers dial, host:port.
listen_portnumberWireGuard listen port, e.g. 51820. Keys are auto-generated if not configured.
prioritynumberBully election priority: higher wins leadership among alive nodes.

[[seeds]]

KeyTypeDescription
endpointstringA peer endpoint gossip contacts first to join the fleet. Repeat the table for multiple seeds.

[[nodes]]

KeyTypeDescription
node_idstringIdentifier of a known fleet member.
endpointstringIts public host:port endpoint.

[[services]]

Static routes. Repeat the table per service; discovered containers add routes alongside these.

KeyTypeDescription
urlstringPublic URL of the service, e.g. https://app.example.com.
node_idstringNode hosting the upstream.
upstreamstringBackend address, e.g. 127.0.0.1:3000. Multiple targets across entries are spread by consistent-hash routing with session affinity.

[services.replicate]

Optional per-service failover duplication: when the hosting node fails, the workload is spawned on a peer via the container driver.

KeyTypeDescription
enabledbooleanTurn on failover duplication for this service.
imagestringContainer image spawned on the peer node.
envtableEnvironment variables passed to the spawned container.
container_portnumberPort the container exposes.
placementstringPlacement rule selecting which peers may host the replica.
failback_modestringmanual or automatic return of the workload once the original node recovers.
failback_cooldown_secsnumberMinimum time before failback runs, preventing flapping.

[handoff]

KeyTypeDescription
modestringIngress handoff tier: none, dns, tunnel, or floating_ip. See handoff tiers.

[handoff.tunnel]

Holds the Cloudflare Tunnel credentials used when mode = "tunnel". The elected leader brings the tunnel up; on re-election the new leader takes it over.

[handoff.dns]

Holds the DNS provider configuration used when mode = "dns": the provider credentials and record BRIDGE updates for health-checked DNS failover.

[sentry]

Optional error reporting.

KeyTypeDescription
dsnstringSentry DSN.
environmentstringEnvironment tag attached to events, e.g. production.
sample_ratenumberFraction of error events sent (0.0–1.0).
traces_sample_ratenumberFraction of traces sent (0.0–1.0).
debugbooleanVerbose Sentry SDK logging.
Environment variable: BRIDGE_CONFIG points the daemon at a config file anywhere on disk and takes precedence over the system path. The installer never overwrites an existing /etc/bridge/bridge.toml.