Skip to content

Backup & disaster recovery

One database holds your durable state — back it up well and recovery is straightforward.

What it is

HelixIAM's persistent state lives in a single PostgreSQL database: realms, users, clients, keys, policy and audit history. helix-iam-server itself holds nothing that needs backing up beyond that database — protecting your deployment comes down to protecting that one database (plus, in one case below, your keystore, and Redis if you rely on session continuity across a Redis restart).

Back up and restore the database

HelixIAM ships no backup scripts of its own (the Helm chart has an example pg_dump CronJob in examples/): use PostgreSQL's own tools, or your managed database's snapshots and point-in-time recovery. A logical backup in PostgreSQL's custom format:

# Backup (custom format, compressed)
pg_dump --host "$DB_HOST" --username "$DB_USERNAME" --dbname helixiam --format=custom --file helixiam.dump

# Restore into an empty database
createdb --host "$DB_HOST" --username "$DB_USERNAME" helixiam_restore
pg_restore --host "$DB_HOST" --username "$DB_USERNAME" --dbname helixiam_restore --no-owner helixiam.dump

On the Docker Compose stack the same tools run inside the postgres container:

docker compose exec -T postgres pg_dump -U helix -d helixiam --format=custom > helixiam.dump

Schedule it with whatever runs your other database jobs (a Kubernetes CronJob, your cloud's managed backups), and test restores.

DB_ENCRYPTION is part of your backup

Sensitive columns are encrypted with DB_ENCRYPTION. A database backup is useless without the exact same key, and the key must never change after first boot. Store DB_ENCRYPTION securely and separately from the database dump, back it up, and never rotate it. See security hardening.

Back up /jks if you seed keys from files

If you seed the master-realm signing key from files, include the /jks keystore in your backup. Without it, a restored database will not match the published JWKS and tokens will fail to verify. See Realm keys.

Realm export — portable config backup

The pg_dump above is your authoritative, byte-for-byte backup. Alongside it, Helix can export a single realm as a self-contained JSON document — realm settings, clients, roles, users, flows, identity providers and more. It is the right tool for migrating a realm between environments, snapshotting config into version control, or seeding a fresh install; it complements (does not replace) the database backup. The examples below assume you have set $HELIX_URL / $REALM and logged in — see Authenticating to the API.

Export a realm — GET /admin/realms/{realm}/export

curl -s -b cookies.txt "$HELIX_URL/admin/realms/$REALM/export" > $REALM-export.json
jq 'keys' $REALM-export.json
[
  "accountConsole", "adminRoles", "agents", "applications",
  "authorizationServices", "clientProtocolMappers", "clientRoles",
  "clientScopes", "clients", "flows", "formatVersion", "groups",
  "identityProviders", "messageTemplates", "messagingProviders",
  "organizations", "realm", "resourceIndicators", "roles", "samlClients",
  "scimTargets", "serviceAccountRoles", "users", "webhooks", "workloadIdentity"
]

The document carries a formatVersion and a requiredEnv list naming the secrets the target environment must supply (identity-provider, webhook, SCIM-target, messaging and CAPTCHA secrets, as HELIX_<REALM>_… variables) — the export never contains those secret values. requiredEnv is null when the realm has none; for a realm gov with a webhook named audit:

jq '{formatVersion, requiredEnv}' $REALM-export.json
{
  "formatVersion": 2,
  "requiredEnv": [ "HELIX_GOV_WEBHOOK_AUDIT_SECRET" ]
}

Re-import — POST /admin/realms/{realm}/import

Restore or seed a realm by POSTing the exported document back (a write, so refresh the CSRF token first):

curl -s -b cookies.txt -c cookies.txt "$HELIX_URL/admin/realms/$REALM/export" >/dev/null
CSRF=$(awk '($6=="__Host-XSRF-TOKEN"||$6=="XSRF-TOKEN")&&$7~/^r1\./{t=$7} END{print t}' cookies.txt)

curl -s -b cookies.txt \
  -H "Content-Type: application/json" -H "X-XSRF-TOKEN: $CSRF" \
  --data-binary @"$REALM-export.json" \
  "$HELIX_URL/admin/realms/$REALM/import"

Config, not durable state

Realm export is a configuration backup, not a substitute for the database dump: it does not carry event history, encrypted credential material, or live sessions. Use it for migration and config-as-code; use pg_dump for full disaster recovery. A Keycloak realm JSON can be imported via POST /admin/realms/{realm}/import/keycloak.

RPO and RTO

  • RPO (recovery point objective) equals your backup interval — the most data you can lose. A nightly backup means up to ~24 h of loss. Tighten the RPO toward zero with WAL archiving / point-in-time recovery (PITR).
  • RTO (recovery time objective) is how long restore takes: provision PostgreSQL, pg_restore the dump, restore /jks if used, and start helix-iam-server and the console. Because the server carries no state outside the database, it returns to service as soon as the database is up.

How to recover

  1. Provision a PostgreSQL instance.
  2. pg_restore the latest dump into it (see above), or restore your managed snapshot.
  3. Restore the /jks keystore if you seed keys from files.
  4. Ensure DB_ENCRYPTION matches the original.
  5. Start helix-iam-server, then the console. With several replicas, restart them all: a replica only reloads identity providers whose version went up, and a restore can lower it.

See also