Skip to content

Upgrades & migrations

Roll forward to a new release with a backward-compatible schema and a zero-downtime deployment order.

What it is

HelixIAM manages its database schema one of two ways:

  • Versioned Flyway migrations (default) — in the app, the container image and the Helm chart alike. Versioned migration scripts are applied on startup, giving you an auditable, ordered migration history.
  • Legacy idempotent bootstrap — the older schema.sql path, kept for existing installs that never adopted Flyway:
HELIX_MIGRATIONS_ENABLED=false
HELIX_SQL_INIT_MODE=always

When an install that used the legacy bootstrap adopts Flyway, set HELIX_FLYWAY_BASELINE_VERSION to that schema's version.

Migration discipline

Migrations are additive and backward-compatible: a new release runs against the previous schema, and the previous release tolerates the new schema. This is what makes rolling upgrades safe.

Never edit a released migration

Once a migration has shipped and been applied anywhere, it is immutable. To change the schema, add a new migration. Editing an applied migration breaks checksum validation and corrupts the migration history.

Zero-downtime rolling upgrade

Not for the upgrade to rc.9

The upgrade from rc.8 (or earlier) to rc.9 moves the server to Spring Boot 4: the old release cannot read sessions and tokens the new one writes, so old and new pods must not run side by side. Use strategy.type=Recreate or scale to zero — see Upgrading to rc.8 / rc.9. The rolling procedure below applies to releases whose notes allow it. The chart's update strategy is the Helm value strategy (default Kubernetes' RollingUpdate, 25% / 25%).

Deploy in this order:

  1. Back up first. Take a fresh database backup — see Backup & disaster recovery.
  2. Roll out helix-iam-server. The first pod/instance to start applies any pending migrations; because migrations are backward-compatible, the still-running old instances keep working against the same schema during the rollout. Session and token state lives in PostgreSQL (and Redis, if configured), not in any one instance, so a rolling replacement keeps signed-in sessions. Short-lived sign-in state (QR sign-in, push approval, transaction signing, device enrolment, CIBA) is shared the same way after rc.12 (see High availability); up to rc.12 it was per replica, so a sign-in in progress on a replaced pod may need to be retried. A release can note other one-off effects — read its upgrade notes.
  3. Deploy the console.

Following this order, the service stays available during an upgrade.

Confirm the running version

Before and after an upgrade, check which release is live and that it came up healthy. /actuator/info is not exposed, so read the version from what you deployed — the image tag:

kubectl get deploy helixiam -o jsonpath='{.spec.template.spec.containers[0].image}'   # Helm
docker compose images server                                                          # Docker Compose

Then confirm readiness. It is anonymous — no session or token — so it works from any host that can reach the server (see Authenticating to the API for $HELIX_URL):

# Readiness after the rollout settles — probe the group, not the aggregate
curl -s "$HELIX_URL/actuator/health/readiness"
{"status":"UP"}

Verify the migration ran

With Flyway enabled, helix-iam-server logs the applied versions on startup, and flyway_schema_history in the database records every applied migration and its checksum. A readiness of UP after the restart confirms the schema reached the shape the new release expects. See Observability & health.

How to upgrade

  1. Read the release notes for the target version — for rc.7 → rc.8 / rc.9, the upgrade guide lists breaking changes (trusted proxies, allowed hosts, encryption migration, write-only IdP secrets); for rc.10 → rc.11, see Upgrading from rc.10 to rc.11 (wallet trusted_authorities rolling upgrades, trusted-list signer grace, Jackson 3, view-identity-providers); for rc.11 → rc.12, see Upgrading from rc.11 to rc.12 (grandfathered identity-provider links and their start-up report, new realm-admin requirements, realm keys and ES256); for rc.12 → rc.13, see Upgrading from rc.12 to rc.13 (realms with enabled=false, DB_ENCRYPTION for the BSN migration and DigiD, changed DigiD usernames, the bsn claim opt-in); from rc.13 to the next release, see Upgrading from rc.13 to 1.1.0-rc.1 (single-use client assertions, wallet mappers, IDP_BASE_URL for the credential issuer, Flyway V90–V135); from 1.1.0-rc.13 to 1.1.0-rc.14, see Upgrading from 1.1.0-rc.13 to 1.1.0-rc.14 (claims only for the scopes granted, end-user-only userinfo, typed claims, a larger proxy header buffer for implicit and hybrid flows); from 1.1.0-rc.1 to the next release, see Upgrading from 1.1.0-rc.1 to 1.1.0-rc.2 (the __Host-XSRF-TOKEN cookie, adminAllowedHosts, the chart's default image, the console image).
  2. Back up the database (pg_dump, see Backup) and keep DB_ENCRYPTION with it.
  3. Roll out helix-iam-server, then the console (a separate Deployment when console.enabled). With Helm, upgrade with helm upgrade --reset-then-reuse-values (Helm 3.14 or later), not --reuse-values: it keeps the values you set and adds the defaults of sections a newer chart introduced. With --reuse-values those sections are missing and the chart refuses to render (values section "..." is missing).
  4. Confirm the deployed image tag and GET /actuator/health/readiness — see Observability & health.

See also