Run C: export/import across 23 → 24 keeps unmanaged attributes, and S15 said it would not
- Database
- PostgreSQL 16, single node, container; docker compose down -v between legs
- Host
- Hetzner CCX33 (fsn1) — 8 vCPU dedicated, 30 GB RAM, local NVMe
Why this run existed
S15 found that in-place upgrading 23 → 24 preserves unmanaged user attributes,
because the migration sets that realm's unmanagedAttributePolicy to ENABLED,
and that a realm created on 24+ silently discards them. It then generalised:
So the danger is not the upgrade. It is every path that produces a new realm on a new version: export/import migration, a rebuilt environment, disaster recovery, or simply a realm created after the upgrade.
Export/import is the most common of those and the one customers choose because it feels safer than an in-place upgrade. We ranked it the untested case most likely to bite a real customer.
It does not bite. The generalisation was wrong, and we would have told a customer otherwise.
Environment
| Field | Value |
|---|---|
| Source | realm expimp created on 23.0.7 through the admin API, 3 users each carrying department and employeeId |
| Export | kc.sh export --dir /exp --realm expimp --users realm_file, run as a one-shot container against the same database |
| Import | kc.sh import --dir /exp into a freshly purged database at each target version |
| Targets | 24.0.5, 25.0.6, 26.0.0, 26.7.1 |
| Database | PostgreSQL 16, single node, container; docker compose down -v between legs |
| Host | Hetzner CCX33 (fsn1) — 8 vCPU dedicated, 30 GB RAM, local NVMe |
| JVM heap | -Xms1g -Xmx4g |
Results
| Leg | Users | USER_ATTRIBUTE rows | Admin API returns them | Accepts a new attribute |
|---|---|---|---|---|
| 23.0.7 as built | 3 | 6 | yes | — |
| 23.0.7 → 23.0.7 (control) | 3 | 6 | yes | — |
| 23.0.7 → 24.0.5 | 3 | 6 | yes | 204, rows 6 → 7 |
| 23.0.7 → 25.0.6 | 3 | 6 | yes | 204, rows 6 → 7 |
| 23.0.7 → 26.0.0 | 3 | 6 | yes | 204, rows 6 → 7 |
| 23.0.7 → 26.7.1 | 3 | 6 | yes | 204, rows 6 → 7 |
| in-place 23 → 24, then export/import within 24.0.5 | 3 | 6 | yes | — |
Nothing is lost, at any target version, and the imported realm continues to
accept unmanaged attributes afterwards — the write test added one and the row
count went to 7. That last column matters: S15's realm-creation failure showed
HTTP 201 with zero rows reaching the database. Here the write lands.
Verification
| Claim | Primary source | Checked |
|---|---|---|
| Attributes survive export/import into 24.0.5 | 6 USER_ATTRIBUTE rows, admin API returns both | ☑ |
| …and into 25.0.6, 26.0.0, 26.7.1 | same query at each version | ☑ |
| The imported realm still accepts new unmanaged attributes | PUT → 204, rows 6 → 7, at all four versions | ☑ |
The import sets unmanagedAttributePolicy=ENABLED | component_config for declarative-user-profile, read directly | ☑ |
| A 23.0.7 export carries the attributes but not the policy | the export JSON, both greps quoted | ☑ |
| Export/import within 23.0.7 is lossless (control) | 6 rows before and after | ☑ |
| Larger realms behave the same | not tested — 3 users | ☐ |
--users different_files / same_file strategies | not tested — only realm_file | ☐ |
Partial export through the admin API rather than kc.sh export | not tested, and it excludes users by default | ☐ |
| Whether a realm created by the admin API on 24+ still loses them | not retested here — S15's result stands as measured | ☐ |