Skip to content

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.

Complete these steps before proceeding:

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):

VariantDatabaseTLSUse when
single-hostEmbedded PostgresNone — port 8080 in the clearPrivate networks, internal staging, developer-laptop testing against a hosts-file entry. Not for the public internet.
with-postgresEmbedded PostgresCaddy auto-TLS via ACME DNS-01Public-facing single-VM deployment. DNS is on a Caddy-supported provider (Cloudflare, Route 53, etc.).
with-postgres-externalExternal (managed or self-run)Caddy auto-TLSCompliance posture requires encryption-at-rest or point-in-time recovery, or you run a managed/external Postgres.
with-postgres-byo-certEmbedded PostgresYour own cert filesDNS 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:

Terminal window
git clone https://github.com/broch-io/broch-deploy.git
cd broch-deploy/docker-compose/<your-variant>
cp .env.example .env
$EDITOR .env # fill in hostname, identity provider, secrets
docker compose up -d --build
curl https://tunnels.company.com/healthz

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 value
ASPNETCORE_URLS # http://0.0.0.0:8080

The 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.

Terminal window
curl https://tunnels.company.com/healthz # liveness — 200 if running
curl 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.

In your variant’s directory:

Terminal window
$EDITOR .env # bump BROCH_VERSION to the new tag
docker compose pull broch
docker compose up -d

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

PortRequired for
80HTTP → HTTPS redirect (Caddy or BYO ingress)
443HTTPS API, tunnel proxy, WebSocket transport

single-host exposes port 8080 directly with no TLS — only open this internally, never from the public internet.

For embedded Postgres:

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

Schedule 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.

From load testing on 0.5 vCPU / 1 GB RAM:

UsersMinimum vCPUMinimum RAM
Up to 1000.51 GB
100–30012 GB
300+24 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.

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

Terminal window
docker compose logs broch

Common 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.

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

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.

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.