Skip to content

Docker Compose Installation

Deploy Broch on any Linux host with Docker. Works on any cloud VM, on-premises server, or bare metal — the most portable option.

Complete these steps before proceeding:

Download the Docker Compose configuration and environment template:

Terminal window
curl -O https://raw.githubusercontent.com/broch-io/broch/main/deploy/docker-compose/docker-compose.yml
curl -O https://raw.githubusercontent.com/broch-io/broch/main/deploy/docker-compose/.env.template
cp .env.template .env

Edit .env and set the required values:

Terminal window
# License key from Broch
BROCH_LICENSE=XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX
# Your wildcard hostname (e.g. tunnels.company.com)
API__WILDCARDHOSTNAME=tunnels.company.com
# Admin email address
Broch__AdminEmail=[email protected]
# Database password (embedded PostgreSQL)
POSTGRES_PASSWORD=a-strong-random-password
# Authentication provider (AzureAd, EntraExternalId, Auth0, or Okta)
AUTHENTICATION__PROVIDER=AzureAd
AUTHENTICATION__CLIENTID=<client-id>
AUTHENTICATION__CLIENTSECRET=<client-secret>
AUTHENTICATION__ADMINROLES=Admin
# Azure AD specific:
AUTHENTICATION__TENANTID=<tenant-id>

See Environment Variables for a complete reference.

Choose one approach.

Section titled “Option A — Caddy (automatic, recommended)”

Caddy obtains and renews the wildcard certificate automatically via ACME DNS-01. No manual certificate management.

Set these additional values in .env:

Terminal window
# DNS provider for automatic TLS (cloudflare or route53)
DNS_PROVIDER=cloudflare
DNS_API_TOKEN=your-dns-provider-api-token

Then start with the Caddy override:

Terminal window
docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d

Broch does not terminate TLS itself — it requires an ingress (reverse proxy) in front of it. The included docker-compose.yml ships with nginx for this purpose, but you can substitute Caddy in static-cert mode, Traefik, or any other reverse proxy that can terminate TLS and forward to broch-server:8080.

Place your wildcard certificate and private key in PEM format:

Terminal window
mkdir -p certs
cp /path/to/fullchain.pem certs/cert.pem
cp /path/to/privkey.pem certs/key.pem

The included nginx configuration mounts certs/ automatically and handles WebSocket proxying. If you bring your own ingress, ensure it forwards Upgrade and Connection headers for WebSocket support.

If you chose Option A (Caddy):

Terminal window
docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d

If you chose Option B (BYO cert):

Terminal window
docker compose up -d

Check server health:

Terminal window
curl https://tunnels.company.com/healthz # liveness — 200 if running
curl https://tunnels.company.com/health/ready # readiness — 200 after license + auth loads

The readiness endpoint returns 503 until the server has loaded its license token and authentication configuration. This typically takes 10–30 seconds on first boot (license activation call to Broch). On subsequent restarts the token is loaded from the database immediately.

For managed databases (RDS, Azure Flexible Server, etc.), see Database, then start with the external DB override:

Terminal window
docker compose -f docker-compose.yml -f docker-compose.external-db.yml up -d
PortRequired for
80HTTP → HTTPS redirect
443HTTPS API, tunnel proxy, WebSocket transport
Terminal window
# Set IMAGE_TAG in .env to the new version
docker compose pull
docker compose up -d

Database migrations run automatically on startup. Active tunnel connections drop on restart — CLI clients reconnect automatically.

Terminal window
docker compose exec postgres pg_dump -U broch brochdb > backup-$(date +%Y%m%d).sql

Schedule this to run daily and store backups off-site. Before upgrading, take a backup so you can roll back if needed.

From load testing on 0.5 vCPU / 1 GB RAM:

UsersMinimum vCPUMinimum RAM
Up to 1000.51 GB
100–30012 GB
300+24 GB

The server hasn’t loaded its license or auth configuration yet. Check logs:

Terminal window
docker compose logs server

Common causes: missing or invalid BROCH_LICENSE, incorrect auth provider settings, or the server can’t reach the Broch central server for license activation.

Verify firewall allows inbound traffic on ports 80 and 443. Check that the wildcard DNS record resolves correctly.

Verify POSTGRES_PASSWORD in .env matches between the postgres and server services.