Install
OpenFault is one container. Everything it keeps (the SQLite database and its generated secret) lives in /data.
OpenFault is in early development. Run it next to Sentry before you switch off Sentry.
docker run
docker run -d --name openfault -p 8080:8080 -v openfault-data:/data ghcr.io/openfault/openfault:latest
Open http://localhost:8080. Nothing needs configuring first: no env vars, no config file.
Setup token
Until the first user exists, the server logs a one-time setup token (ofs_…) at boot and shows it on /setup:
no users yet. Open /setup, or bootstrap with this one-time setup token (POST /api/v1/setup or the MCP tool setup_instance): ofs_…
Read it with docker logs openfault. It bootstraps the server without the browser, over the REST API or MCP. Set OPENFAULT_SETUP_TOKEN to choose it yourself. It stops working once the first user exists, whichever path created it.
Setup wizard
- Organization, owner, project. The org name, your name, email and password (10+ characters), and the first project’s name and platform.
- Email & notifications (optional, skippable). Base URL, SMTP with an Amazon SES preset and a Send test email button, and the push relay. See Email and Push & iOS app.
- Finish. You land on the project’s SDK setup page with its DSN.
Everything from step 2 stays editable under Settings → Organization.
Health check
The image has a Docker HEALTHCHECK built in. It runs openfault healthcheck, which GETs /healthz on 127.0.0.1:$OPENFAULT_PORT and exits 0 on HTTP 200, else 1. The image is distroless (no shell, no curl), so use the same command from your orchestrator:
docker exec openfault /openfault healthcheck
Compose
services:
openfault:
image: ghcr.io/openfault/openfault:latest
ports:
- "8080:8080"
volumes:
- openfault-data:/data
restart: unless-stopped
volumes:
openfault-data:
Environment variables
All optional. They override what is set in the wizard and Settings → Organization; a field set by the environment shows “set by environment” and is locked in the UI.
| Variable | Default | Meaning |
|---|---|---|
OPENFAULT_DATA | /data in the image (./data outside it) | Directory for openfault.db and the generated secret. |
OPENFAULT_PORT | 8080 | HTTP listen port. |
OPENFAULT_BIND | :: | Listen address. The default is dual-stack (IPv4 and IPv6) and falls back to 0.0.0.0 without IPv6. |
OPENFAULT_BASE_URL | Settings, else the Host header | Public URL for DSNs and links in emails, pushes and webhooks. An https:// value also marks session cookies Secure. |
OPENFAULT_SMTP_URL | Settings | Outgoing email as one URL. See Email. |
OPENFAULT_PUSH_RELAY_URL | Settings | Push relay for the iOS app. See Push & iOS app. |
OPENFAULT_PUSH_RELAY_TOKEN | Settings | Bearer token for that relay. |
OPENFAULT_TRUST_PROXY | off | 1 uses the first X-Forwarded-For address for login and pairing rate limits. Set it only behind your own proxy. |
OPENFAULT_SETUP_TOKEN | random | Pre-set the one-time setup token, for automated installs. |
OPENFAULT_ALLOW_PRIVATE_WEBHOOKS | Settings (off) | 1 allows webhook URLs on private, local and loopback addresses. |
OPENFAULT_DEV_DSN_KEY | off | Debug builds only: seeds a dev project with this DSN key. Ignored in release images. |
RUST_LOG | info | Log filter. |
Bind mounts
The container runs as the distroless nonroot user (uid 65532). A named volume (as above) inherits the right owner. A bind mount keeps the host’s ownership, so give it to that user first:
mkdir -p ./data && sudo chown 65532:65532 ./data
docker run -d --name openfault -p 8080:8080 -v "$PWD/data":/data ghcr.io/openfault/openfault:latest
Reverse proxy (HTTPS and HTTP/2)
Put OpenFault behind a proxy that terminates HTTPS. HTTPS gets you HTTP/2, and that matters: every open tab holds one live-update connection, and browsers on HTTP/1.1 allow only 6 per host, so the seventh tab stalls.
Caddy does it in three lines and gets the certificate for you:
errors.example.com {
reverse_proxy localhost:8080
}
Then set the base URL to https://errors.example.com (in Settings → Organization, or OPENFAULT_BASE_URL) so DSNs and links use the public address.
Upgrade
Pull the new image and restart. Migrations run on boot.
docker pull ghcr.io/openfault/openfault:latest
docker stop openfault && docker rm openfault
docker run -d --name openfault -p 8080:8080 -v openfault-data:/data ghcr.io/openfault/openfault:latest
With Compose: docker compose pull && docker compose up -d.
Backup
Built-in backups are coming in the next release. Until then, use the steps below.
The database runs in SQLite WAL mode, so a copy taken while it is writing can miss the newest pages. The image is distroless and has no sqlite3 shell. The safe path: stop, copy the volume, start.
docker stop openfault
docker run --rm -v openfault-data:/data -v "$PWD":/backup alpine tar czf /backup/openfault-$(date +%F).tgz -C /data .
docker start openfault
SDKs retry while it is down, and the stop takes seconds. Restore by extracting the archive into an empty volume. Upgrade and backup notes in the README are still being written; check it before a major upgrade.