The Keycloak Upgrade Ledger

Every upgrade we have rehearsed, with the environment stated and the clock running.

2026-08-31 · run 2026-08-31-k8s-infinispan-rolling-update-26.7.3

Kubernetes / Infinispan rolling update at 26.7.3

Upgrade
26.0.0 → 26.7.3
Scale
master realm + lab realm (1 user)
Database
PostgreSQL 16, single pod (StatefulSet), in-cluster
Topology
Deployment replicas: 2, maxSurge=1, maxUnavailable=0
Host
laptop (16 vCPU, 31 GB RAM)
Condition
Kubernetes / Infinispan, re-run at the 26.7.3 target

Environment

FieldValue
Keycloak from → to26.0.0 → 26.7.3
Distribution / start modequay.io/keycloak/keycloak, start w/ external DB
DatabasePostgreSQL 16, single pod (StatefulSet), in-cluster
Cacheembedded Infinispan, KC_CACHE_STACK=kubernetes (DNS_PING)
Clusterkind kclab (k8s v1.37.0, kind v0.33.0) — note: laptop, not the k3d box
TopologyDeployment replicas: 2, maxSurge=1, maxUnavailable=0
Hostlaptop (16 vCPU, 31 GB RAM)
JVM heap-Xms256m -Xmx1g
Datasetmaster realm + lab realm (1 user)

Result — rolling update completes cleanly, cache still partitions

Timeline (from pod logs):

tevent
0sset image → new 26.7.3 pod (Infinispan v=16.0.14) starts, old 26.0.0 pods (v=15.0.8) still serving
~25snew pod forms a 1-member cluster (v=16.0.14); no members discovered
~36ssecond new pod starts; first new pod ready; first old pod terminated
~47sthe two new pods form a 2-member cluster (v=16.0.14)
~57slast old pod terminated; rollout complete
endmigration_model = 26.7.3, both pods ready

Findings

  1. Schema migration stays additive at 26.7.3migration_model reached 26.7.3, old pods logged no schema errors, rollout completed under maxUnavailable=0. The database layer is safe; the cache is the risk, as at 26.7.1.

  2. The cache still partitions during the transition. 26.7.3 ships Infinispan 16.0.14 (26.7.1 was 16.0.12; 26.0.0 is 15.0.8). The new pods never merge with the old v=15.0.8 cluster — they form a separate 1-member cluster and only merge with each other once the old pods are gone. Same operational consequence as the 26.7.1 record: sessions are not shared between old and new pods during the window.

  3. New signal: 26.7.3 enables JGroups encryption (mTLS). The 26.7.3 pod logs JGroups Encryption enabled (mTLS). This is a second axis of inter-version incompatibility in addition to the Infinispan/JGroups version bump, and belongs in the pre-flight as a thing to check when crossing into 26.7.x.

  4. kind vs k3d: a discovery race at first boot. On kind, two pods started simultaneously each became a 1-member cluster (no members discovered after 2.00s) and did not merge on their own; deleting one pod triggered DNS_PING re-discovery and the 2-member cluster formed. The k3d run (original record) clustered on first boot. This is an environment/timing difference, not a Keycloak change — but it means the baseline "2-member cluster" must be verified explicitly before relying on a kind run.

  5. Cross-pod session sharing confirmed at the final 26.7.3 state — a refresh token issued on pod A was accepted on pod B (HTTP 200), proving the healed 2-member cluster shares the session cache.

What is still not tested

← All runs