Skip to main content
Openfuse

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:

RecordTypeValue
example.comA<server-ip>
*.example.comA<server-ip>
*.api.example.comA<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.com

All three should return your server's IP.

2. Download and configure

Using the installer

curl -sSL https://get.openfuse.io/install | bash

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

Manual setup

Download and extract the distribution bundle:

tar -xzf openfuse-self-hosted-<version>.tar.gz -C openfuse/
cd openfuse/
cp .env.example .env

Edit .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-password

Run the validation script:

bash scripts/setup.sh

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

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:

  1. Create two databases on your PostgreSQL instance: openfuse and keycloak
  2. Remove the bundled database profile in .env:
    #COMPOSE_PROFILES=bundled-db
  3. Configure the connection:
    DATABASE_HOST=your-rds-endpoint.amazonaws.com
    DATABASE_PORT=5432
    DATABASE_USER=openfuse
    DATABASE_PASSWORD=your-password
    DATABASE_SSL=true
  4. 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.

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=Openfuse

Set 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-secret

Same pattern for KC_MICROSOFT_* and KC_GITHUB_*. See the configuration reference for all options.

7. Start Openfuse

docker compose up -d

First startup takes a few minutes. Monitor progress:

docker compose logs -f

Once all services are healthy (docker compose ps):

  1. Go to https://<ROOT_DOMAIN>
  2. Click Sign in
  3. Log in with ROOT_USER_EMAIL and ROOT_USER_PASSWORD
  4. 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).sql

Managed 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).sql

For 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.sql

Always 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 latest in production. Set OPENFUSE_VERSION=1.2.0 in .env.
  • Rotate secrets — rotate SESSION_SECRET and client secrets periodically. Rotating SESSION_SECRET invalidates 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 .env files. 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
ProblemCheck
Services won't startdocker compose logs <service> for error details
Keycloak config import failsVerify all KC_* env vars are set. Check docker compose logs keycloak-config-importer
API can't connect to databaseVerify DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD
TLS certificate issuesEnsure DNS is propagated (dig +short your-domain) and ports 80/443 are open

Next steps

On this page