Docker Compose Installation
Deploy Broch on any Linux host with Docker. The most portable option — works on cloud VMs, on-premises servers, or bare metal.
The actual compose files live in broch-io/broch-deploy under docker-compose/. This page covers the decisions before you clone; each compose variant has its own README with the platform-specific setup steps.
Prerequisites
Section titled “Prerequisites”Complete these steps before proceeding:
- Docker Engine 24+ with Compose V2 installed
- Wildcard domain & DNS configured
- TLS certificate approach decided — Caddy automatic, BYO cert, or none
- Identity provider app registration created
- License — activated in-app after first sign-in (nothing to set before deploy)
Pick a variant
Section titled “Pick a variant”Four compose variants cover the common shapes. They differ along two axes — where the database lives (embedded sidecar vs. external/managed) and where TLS terminates (Caddy auto-ACME, your own cert, or none):
| Variant | Database | TLS | Use when |
|---|---|---|---|
single-host | Embedded Postgres | None — port 8080 in the clear | Private networks, internal staging, developer-laptop testing against a hosts-file entry. Not for the public internet. |
with-postgres | Embedded Postgres | Caddy auto-TLS via ACME DNS-01 | Public-facing single-VM deployment. DNS is on a Caddy-supported provider (Cloudflare, Route 53, etc.). |
with-postgres-external | External (managed or self-run) | Caddy auto-TLS | Compliance posture requires encryption-at-rest or point-in-time recovery, or you run a managed/external Postgres. |
with-postgres-byo-cert | Embedded Postgres | Your own cert files | DNS isn’t on a Caddy-supported provider, you use central PKI / commercial CA, or you’re in a restricted-egress network. |
Each variant’s README walks the platform-specific steps. The high-level flow is the same:
git clone https://github.com/broch-io/broch-deploy.gitcd broch-deploy/docker-compose/<your-variant>
cp .env.example .env$EDITOR .env # fill in hostname, identity provider, secrets
docker compose up -d --buildcurl https://tunnels.company.com/healthzEnvironment variables
Section titled “Environment variables”The full env-var contract is the same across all four variants — Broch reads from the same configuration regardless of which compose file fronts it. Key ones:
BROCH_MASTER_KEY # at-rest encryption root (required; generate with `openssl rand -base64 48`)API__WILDCARDHOSTNAME # wildcard hostname (required)ConnectionStrings__DefaultConnection # Postgres connection string (required)AUTHENTICATION__* # identity provider (boot floor — see env-var reference)DATABASE__PROVIDER # PostgreSQL is the only supported valueASPNETCORE_URLS # http://0.0.0.0:8080The license is not an env var — it’s activated in-app on first sign-in (Configuration → License).
The .env.example template each variant ships with maps user-facing values (e.g. BROCH_WILDCARD_HOSTNAME, POSTGRES_PASSWORD) to the underlying Broch env vars in the compose file. You set the user-facing ones; the compose handles the substitution.
See Environment Variables for the full reference.
Verify
Section titled “Verify”curl https://tunnels.company.com/healthz # liveness — 200 if runningcurl https://tunnels.company.com/healthz/ready # readiness — 200 after license + auth loads/healthz returns 200 as soon as the process is running. /healthz/ready returns 503 until an admin completes First-Run Setup in the browser — that is normal; proceed to sign in. See Health Checks for the full endpoint reference.
Upgrading
Section titled “Upgrading”In your variant’s directory:
$EDITOR .env # bump BROCH_VERSION to the new tagdocker compose pull brochdocker compose up -dDatabase migrations run automatically on startup. Active tunnel connections drop on restart — CLI clients reconnect automatically.
Firewall requirements
Section titled “Firewall requirements”| Port | Required for |
|---|---|
| 80 | HTTP → HTTPS redirect (Caddy or BYO ingress) |
| 443 | HTTPS API, tunnel proxy, WebSocket transport |
single-host exposes port 8080 directly with no TLS — only open this internally, never from the public internet.
Backup
Section titled “Backup”For embedded Postgres:
docker compose exec postgres pg_dump -U broch brochdb > backup-$(date +%Y%m%d).sqlSchedule this daily, store off-site. Before upgrading, take a backup. For managed Postgres (the with-postgres-external variant), use your provider’s snapshot/backup tooling instead — that’s one of the reasons to choose it.
Capacity
Section titled “Capacity”From load testing on 0.5 vCPU / 1 GB RAM:
| Users | Minimum vCPU | Minimum RAM |
|---|---|---|
| Up to 100 | 0.5 | 1 GB |
| 100–300 | 1 | 2 GB |
| 300+ | 2 | 4 GB |
Horizontal clustering (multiple Broch replicas behind one load balancer) is not available in the current release — scale vertically using the sizing above, or run independent instances per team/region. Clustering is on the roadmap; contact [email protected] if you need it.
Troubleshooting
Section titled “Troubleshooting”Health check returns 503
Section titled “Health check returns 503”The server hasn’t loaded its license or auth configuration. Check logs:
docker compose logs brochCommon causes: the license hasn’t been activated yet (do it in-app under Configuration → License), incorrect auth-provider settings, or the server can’t reach the Broch central server for license activation.
Tunnels won’t connect
Section titled “Tunnels won’t connect”Verify firewall allows inbound traffic on 80/443. Check that the wildcard DNS record resolves correctly.
Database connection fails (embedded)
Section titled “Database connection fails (embedded)”Verify POSTGRES_PASSWORD in .env matches between the postgres and broch services — both are templated from the same value but only if you didn’t hand-edit one of them.
Database connection fails (external)
Section titled “Database connection fails (external)”Check BROCH_DB_CONNECTION_STRING against your DB’s actual TLS mode (SSL Mode=Require vs Prefer vs Disable), firewall rules, and that the database actually exists on the server. Broch logs the underlying Npgsql error — docker compose logs broch | grep -i postgres.