Docker Compose
Deploy Openfuse to production with managed databases, wildcard TLS, SMTP, and automated backups. A complete guide for running Openfuse reliably on your own infrastructure.
This guide walks you through a production-ready Openfuse deployment on a single server using Docker Compose. For container orchestrators, see Kubernetes, AWS ECS, Cloud Run, or Azure Container Apps.
If you're evaluating Openfuse, start with Try Locally or Deploy to Cloud first.
Production checklist
Before you begin, make sure you have:
- A Linux server with Docker and Docker Compose v2
- A domain with DNS access (wildcard records required)
- A PostgreSQL 17+ instance (managed recommended) — or use the bundled one
- An SMTP server for transactional emails
- Ports 80 and 443 open for HTTPS traffic
1. DNS configuration
Openfuse uses subdomain-based routing. You need three DNS records pointing to your server:
| Record | Type | Value |
|---|---|---|
example.com | A | <server-ip> |
*.example.com | A | <server-ip> |
*.api.example.com | A | <server-ip> |
Replace example.com with your ROOT_DOMAIN.
Verify propagation:
dig +short example.com
dig +short anything.example.com
dig +short anything.api.example.comAll three should return your server's IP.
2. Download and configure
Using the installer
curl -sSL https://get.openfuse.io/install | bashThe installer walks you through domain, credentials, database, and SMTP configuration. It generates all secrets and validates your setup before starting.
To install a specific version:
curl -sSL https://get.openfuse.io/install | bash -s -- --version 1.2.0Manual setup
Download and extract the distribution bundle:
tar -xzf openfuse-self-hosted-<version>.tar.gz -C openfuse/
cd openfuse/
cp .env.example .envEdit .env and fill in all required values:
# Your domain
ROOT_DOMAIN=example.com
# Pin the version (never use "latest" in production)
OPENFUSE_VERSION=1.2.0
# Database — generate a strong password
DATABASE_PASSWORD=$(openssl rand -hex 16)
# Session secret — 32+ characters
SESSION_SECRET=$(openssl rand -hex 32)
# Keycloak admin
KC_BOOTSTRAP_ADMIN_PASSWORD=$(openssl rand -hex 16)
# Client secrets — generate unique values for each
KC_STAFF_BACKEND_CLIENT_SECRET=$(openssl rand -hex 24)
KC_STAFF_BFF_CLIENT_SECRET=$(openssl rand -hex 24)
KC_TENANTS_BACKEND_CLIENT_SECRET=$(openssl rand -hex 24)
KC_TENANTS_BFF_CLIENT_SECRET=$(openssl rand -hex 24)
KC_TENANTS_SDK_CLIENT_SECRET=$(openssl rand -hex 24)
# Root admin user
ROOT_USER_EMAIL=admin@example.com
ROOT_USER_PASSWORD=YourStr0ng!Password
# SMTP
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-smtp-user
SMTP_PASSWORD=your-smtp-passwordRun the validation script:
bash scripts/setup.shThis checks Docker, environment variables, DNS resolution, and Keycloak config files.
3. Database
Bundled PostgreSQL (simple)
The default compose.yml includes a PostgreSQL container. It creates both databases (openfuse and keycloak) automatically via the init-db.sh script. This works but means your database lives inside a Docker volume on the same server.
Managed database (recommended)
For production, use a managed PostgreSQL 17+ service (Amazon RDS, Google Cloud SQL, Azure Database for PostgreSQL). Benefits: automated backups, point-in-time recovery, high availability, and monitoring out of the box.
Setup:
- Create two databases on your PostgreSQL instance:
openfuseandkeycloak - Remove the bundled database profile in
.env:#COMPOSE_PROFILES=bundled-db - Configure the connection:
DATABASE_HOST=your-rds-endpoint.amazonaws.com DATABASE_PORT=5432 DATABASE_USER=openfuse DATABASE_PASSWORD=your-password DATABASE_SSL=true - If Keycloak uses the same server (recommended):
KC_DB_HOST=your-rds-endpoint.amazonaws.com KC_DB_USERNAME=openfuse KC_DB_PASSWORD=your-password
The database user must have permission to create schemas and tables in the openfuse database. The API handles all schema setup on first run.
For Keycloak with SSL, you may need to set the full JDBC URL: KC_DB_URL=jdbc:postgresql://host:5432/keycloak?sslmode=require
4. TLS / HTTPS
Caddy handles TLS automatically.
Standard certificates (default)
The default configuration uses ACME HTTP-01 challenge. Caddy obtains individual certificates for each hostname as traffic hits them. This works out of the box — no configuration needed.
Wildcard certificates (recommended)
For multi-tenant routing, wildcard certificates avoid per-subdomain certificate requests. This requires a DNS challenge provider.
The Caddyfile includes commented examples for configuring DNS providers (Cloudflare, Route53, etc.) and instructions for building a custom Caddy image with the appropriate DNS plugin.
5. SMTP
SMTP is required for production — without it, team invites and password reset emails won't be sent.
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-smtp-user
SMTP_PASSWORD=your-smtp-password
SMTP_FROM_DOMAIN=example.com
SMTP_FROM_NAME=OpenfuseSet SMTP_SECURE=true and SMTP_PORT=465 for providers that require implicit TLS (e.g., Resend).
6. Social login (optional)
Enable Google, Microsoft, or GitHub OAuth on the login page:
KC_GOOGLE_ENABLED=true
KC_GOOGLE_CLIENT_ID=your-google-client-id
KC_GOOGLE_CLIENT_SECRET=your-google-client-secretSame pattern for KC_MICROSOFT_* and KC_GITHUB_*. See the configuration reference for all options.
7. Start Openfuse
docker compose up -dFirst startup takes a few minutes. Monitor progress:
docker compose logs -fOnce all services are healthy (docker compose ps):
- Go to
https://<ROOT_DOMAIN> - Click Sign in
- Log in with
ROOT_USER_EMAILandROOT_USER_PASSWORD - Follow the onboarding flow to create your first company
8. Logging
Set the log level via LOG_LEVEL in .env (debug, info, warn, error). The API outputs structured JSON logs.
9. Backups
Back up both databases regularly. A restore requires both to be consistent.
Bundled PostgreSQL
docker compose exec db pg_dump -U openfuse -d openfuse > openfuse-$(date +%Y%m%d).sql
docker compose exec db pg_dump -U openfuse -d keycloak > keycloak-$(date +%Y%m%d).sqlManaged database
pg_dump -h <host> -U <user> -d openfuse > openfuse-$(date +%Y%m%d).sql
pg_dump -h <host> -U <user> -d keycloak > keycloak-$(date +%Y%m%d).sqlFor managed databases, also use your provider's snapshot and point-in-time recovery features.
Restore
psql -h <host> -U <user> -d openfuse < openfuse-YYYYMMDD.sql
psql -h <host> -U <user> -d keycloak < keycloak-YYYYMMDD.sqlAlways back up before upgrading. Database migrations run automatically on API startup and are not reversible without a backup.
10. Security hardening
- Pin image versions — never use
latestin production. SetOPENFUSE_VERSION=1.2.0in.env. - Rotate secrets — rotate
SESSION_SECRETand client secrets periodically. RotatingSESSION_SECRETinvalidates all active sessions. - Firewall — only expose ports 80 and 443. Block direct access to PostgreSQL (5432) and Keycloak (8080) from the internet.
- Secrets management — store secrets in your platform's secret manager rather than in
.envfiles. See the configuration reference for examples.
Troubleshooting
# Service status
docker compose ps
# View logs
docker compose logs api
docker compose logs keycloak
docker compose logs keycloak-config-importer
# Restart a service
docker compose restart api| Problem | Check |
|---|---|
| Services won't start | docker compose logs <service> for error details |
| Keycloak config import fails | Verify all KC_* env vars are set. Check docker compose logs keycloak-config-importer |
| API can't connect to database | Verify DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD |
| TLS certificate issues | Ensure DNS is propagated (dig +short your-domain) and ports 80/443 are open |
Next steps
Deploy to Cloud
Deploy a self-hosted Openfuse instance on a cloud VM or from a CI pipeline. Non-interactive setup with automatic secret generation for team evaluation and staging environments.
Kubernetes
Deploy Openfuse on Kubernetes with Deployments, Jobs, Secrets, and Ingress. Includes manifests for the API, Keycloak, UI, and the Config Importer as a Helm hook.