Quick and easy upgrade of Keycloak over multiple major versions

by Konstantin Troshin |
Oct 15, 2025 |
Automation of your software development and IT infrastructure | Cloud Technologies | Cloud Technology Consulting & Implementation | Containerization

Recently, we had a recurring project with a client who had Keycloak 14 still running as their Identity Provider (IdP) in their webshop. As an open-source identity and access management solution for modern applications and services, Keycloak is built on top of industry security standard protocols. Considering that the current version of Keycloak was at 21 or 22 at the beginning of the project, we tried to urge the client to upgrade to the latest version to avoid vulnerabilities and profit from the new features. Recently, the client finally agreed to the upgrade, so we started planning it.

The goal was to bring Keycloak from v14.0.0 to v26.3.2 (the latest version at that time). One of the strategies was to do a step-by-step upgrade going over the major versions. This, however, would require some time effort and possible debugging (error analysis) in the middle of the way if something went wrong.

So, we thought, what if we export from the old Keycloak and import into the new version instead? Keycloak has had the export and import functionalities for a while now, so this strategy appeared quite promising. Apart from potential speed of the migration, there appeared to be another big advantage: the running V14.0.0 could be kept running upon migration and testing and would be available for a fast rollback if something went wrong.

Generally, Keycloak has two ways of exporting realms: via the UI or by using kc.sh or its older counterparts directly on the server (in the container). However, the UI option was not viable, since it does not export users and they were the critical part of the migration. Thus, we decided to go with direct export / import in the containers.

First, I analyzed the plugins and themes that were in use for compatibility with v26. It turned out, that only the plugin developed by me (to allow user verification by SMS as alternative to the classic e-mail flow) needed to be refactored. Once finished, I tested the new plugin and verified that it worked (even better than before, since required actions are configurable now since V25.0.0). The custom theme the client had was also quickly adapted to V26, so that the new Keycloak was ready to receive the realms.

Next step was the export/import itself, which I tested first on the test environment of the client. Running this:

function runExport() {
docker exec -it ${CONTAINER_NAME} /opt/jboss/keycloak/bin/standalone.sh \
-Djboss.socket.binding.port-offset=100 \
-Dkeycloak.migration.action=export \
-Dkeycloak.migration.provider=singleFile \
-Dkeycloak.migration.realmName=$1 \
-Dkeycloak.migration.usersExportStrategy=REALM_FILE \
-Dkeycloak.migration.file=/tmp/${1}.json
}

on the server running the old Keycloak version, copying the file out of the container, mounting it to a new container (running V26) and finally running (${1} is the name of the realm to be migrated):

${_docker} exec -it \
${CONTAINER_NAME} \
/opt/keycloak/bin/kc.sh \
import –file /exports/${1}.json \
–verbose

This worked fine on the test system and the users showed up there as expected. After some extensive testing of the apps connected to the IdP we decided to move onto production.

It turned out, however, that Keycloak 14 fails to export users into a single file if their number is high enough (> ~2000). Thus, I had to switch to the directory strategy:

docker exec -it ${CONTAINER_NAME} /opt/jboss/keycloak/bin/standalone.sh \
-Djboss.socket.binding.port-offset=100 \
-Dkeycloak.migration.action=export \
-Dkeycloak.migration.provider=dir \
-Dkeycloak.migration.realmName=$1 \
-Dkeycloak.migration.usersExportStrategy=REALM_FILE \
-Dkeycloak.migration.dir=/tmp/${1}

(also here, ${1} is the name of the realm to migrate)

Then I copied the whole directories using docker cp and moved it to the new server. After this, the realms could be imported by using (the folder with exports is mounted as /exports).

#!/bin/bash
docker exec -it \
${CONTAINER_NAME} \
/opt/keycloak/bin/kc.sh \
import –dir /exports/${1} \
–verbose

This simple strategy allowed us to quickly migrate realms over 12 major versions of Keycloak and 3 major versions of PostgreSQL while preserving the original containers as backup options for rollback.

You can find more information here: Cloud-Technologieberatung und Umsetzung – fme AGCloud-Technologieberatung und Umsetzung – fme AG

Have we aroused your interest? Then please feel free to write to us.
CONTACT US NOW
×

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *