Running a Keycloak service in a Docker container allows us to share its configuration across multiple environments. However, we can also export an entire Keycloak realm in case we need any backups or data transfer between servers.
For reference, below you’ll find the resulting directory tree for this example:
In short, my goal is to get the export
directory containing realm resources (in this case, the keep-growing-realm.json
file). Thanks to this, I’ll be able to backup or recreate a complete realm whenever I need to.
The Keycloak Admin Console provides an easy way to export a realm. However, as you can see in the screenshot below, not all resources can be exported with this method:
The realm-export.json
file produced with this technique won’t contain user data. In addition, client secrets will be masked. Although this approach might be appropriate in some use cases, we won’t be able to recreate the same instance using only this file.
If we want to get realm data suitable for backups or cross server migration, we need to run a boot-time export.
I have my keep-growing
realm with the following users:
Furthermore, the realm contains the spring-boot-example-app
client. It has the confidential access type and a secret, as you can see in the screenshot below:
At the end of this article, I will verify that the users and the secret are properly exported.
I’m going to use a volume to map exported data between the container and my machine. Next, I will show you how to export the data with a command or automatically when running the container. Finally, I’m going to verify if the export was successful.
First, we need to start our container with a volume that will hold exported data. Therefore, I’m going to map the ./keycloak/realms/export
folder on my machine to the /tmp/export
directory in the keycloak service. Below you’ll find my docker-compose.yml
file:
version: '3.3'
services:
keycloak:
image: jboss/keycloak:${KEYCLOAK_VERSION}
ports:
- "8024:8080"
environment:
- KEYCLOAK_USER=${KEYCLOAK_USER}
- KEYCLOAK_PASSWORD=${KEYCLOAK_PASSWORD}
volumes:
- ./keycloak/realms/export:/tmp/export
I’m using my default environmental variables defined in the following .env
file:
KEYCLOAK_VERSION=16.1.1
KEYCLOAK_USER=keycloak
KEYCLOAK_PASSWORD=keycloak
Now I’m going to start the service with the docker-compose up -d
command. As we can see in the following screenshot, the volume is properly created and mapped:
Obviously, the /tmp/export
directory inside the dockerized Keycloak instance is empty as we still need to export our realm:
Let me introduce the properties that we’re going to use:
Djboss.socket.binding.port-offset
, we want to run the export on a different port than Keycloak itself. By default, the jboss server in my kecyloak
instance uses the 9990
port. Therefore, providing e.g. 100 as a value here will result in the command using the 10090
port.Dkeycloak.migration.action
, we specify what we want to do. Available options:export
,import
.Dkeycloak.migration.provider
, we define how we want the data. Available options are:singleFile
, if we want to export data into a filedir
, if we want to export data into a directory.Dkeycloak.migration.realmName
, realm for export. Don’t specify this parameter if you want to export all realms.Dkeycloak.migration.usersExportStrategy
, how to export realm users. Available options are:keep-growing-realm.json
and keep-growing-users.json
).Dkeycloak.migration.file
, the file where we want to save data. Remember to use the exact same value as you used for the volume mapping.We’re going to execute the /opt/jboss/keycloak/bin/standalone.sh
script included in the jboss/keycloak image to initiate the export process:
docker exec -it <CONTAINER NAME> /opt/jboss/keycloak/bin/standalone.sh \
… // required Keycloak export properties
To clarify the following examples, my example keycloak
service runs in the keycloakspringboot_keycloak_1
container.
When a realm doesn’t contain a lot of users it might be appropriate to export it into a single file. The actual command that I’m going to execute looks like in the snippet below:
docker exec -it keycloakspringboot_keycloak_1 /opt/jboss/keycloak/bin/standalone.sh \
-Djboss.socket.binding.port-offset=100 \
-Dkeycloak.migration.action=export \
-Dkeycloak.migration.provider=singleFile \
-Dkeycloak.migration.realmName=keep-growing \
-Dkeycloak.migration.usersExportStrategy=REALM_FILE \
-Dkeycloak.migration.file=/tmp/export/keep-growing-realm.json
If the command encounters no problems, you’ll see console output similar to this:
…
12:11:10,399 INFO [org.keycloak.services] (ServerService Thread Pool -- 60) KC-SERVICES0034: Export of realm 'keep-growing' requested.
12:11:10,400 INFO [org.keycloak.exportimport.singlefile.SingleFileExportProvider] (ServerService Thread Pool -- 60) Exporting realm 'keep-growing' into file /tmp/export/keep-growing-realm.json
12:11:10,790 INFO [org.keycloak.services] (ServerService Thread Pool -- 60) KC-SERVICES0035: Export finished successfully
…
12:11:11,195 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0060: Http management interface listening on http://127.0.0.1:10090/management
12:11:11,196 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin console listening on http://127.0.0.1:10090
As a result, a new file appears in the keycloak/realms/export
directory on my machine:
You can quit the process with Ctr+C
.
If you want to import that realm, the Keycloak in Docker #2 – How to import a Keycloak realm article describes in detail importing a realm from a file.
Keycloak recommends exporting data into a directory if your realm contains more than 500 users:
Exporting many users into a directory performs optimally as the directory provider uses a separate transaction for each “page” (a file of users).
https://www.keycloak.org/docs/16.1/server_admin/#assembly-exporting-importing_server_administration_guide
In this case, my command would look like this (notice changes in lines 4 and 6):
docker exec -it keycloakspringboot_keycloak_1 /opt/jboss/keycloak/bin/standalone.sh \
-Djboss.socket.binding.port-offset=100 \
-Dkeycloak.migration.action=export \
-Dkeycloak.migration.provider=dir \
-Dkeycloak.migration.realmName=keep-growing \
-Dkeycloak.migration.dir=/tmp/export
If the command encounters no problems, you’ll see console output similar to this:
…
12:29:31,726 INFO [org.keycloak.services] (ServerService Thread Pool -- 53) KC-SERVICES0034: Export of realm 'keep-growing' requested.
12:29:32,107 INFO [org.keycloak.exportimport.dir.DirExportProvider] (ServerService Thread Pool -- 53) Realm 'keep-growing' - data exported
12:29:32,212 INFO [org.keycloak.exportimport.dir.DirExportProvider] (ServerService Thread Pool -- 53) Users 0-3 exported
12:29:32,214 INFO [org.keycloak.services] (ServerService Thread Pool -- 53) KC-SERVICES0035: Export finished successfully
…
As a result, a group of new files appear in the keycloak/realms/export
directory on my machine:
You can quit the process with Ctr+C
.
If you want to import that realm, see the Keycloak in Docker #6 – How to import realms from a directory article as it describes in detail importing realms exported to a folder.
Additionally, we can configure our docker-compose.yml
file to initiate the export when we start the container (by adding the command config). It can be useful when our docker image doesn’t provide an equivalent to the standalone.sh
script or we just don’t want to use it. The modified compose file for a single file export looks like this:
version: '3.3'
services:
keycloak:
image: jboss/keycloak:${KEYCLOAK_VERSION}
ports:
- "8024:8080"
environment:
- KEYCLOAK_USER=${KEYCLOAK_USER}
- KEYCLOAK_PASSWORD=${KEYCLOAK_PASSWORD}
volumes:
- ./keycloak/realms/export:/tmp/export
command:
- "-Dkeycloak.migration.action=export"
- "-Dkeycloak.migration.provider=singleFile"
- "-Dkeycloak.migration.realmName=keep-growing"
- "-Dkeycloak.migration.usersExportStrategy=REALM_FILE"
- "-Dkeycloak.migration.file=/tmp/export/keep-growing-realm.json"
We’ll see in logs that the container starts with the provided options:
The export will proceed as in the previously described methods.
We’re going to find the secret generated for the spring-boot-example-app
client in the realm config file:
Furthermore, the users are included in the exported assets as well. In case of the directory export the users are stored in the keep-growing-users-0.json
file. For the single file export, the users are included directly in the keep-growing-realm.json
file:
What to check when the export didn’t work as planned?
If you see the NullPointerException
error in the logs make sure that the Dkeycloak.migration.realmName
property value is consistent with the actual name of the realm you want to export. The following example error shows what happens if I provide non-existing
to the command but my realm’s id is actually keep-growing
:
11:42:20,795 INFO [org.keycloak.services] (ServerService Thread Pool -- 54) KC-SERVICES0034: Export of realm 'non-existing' requested.
11:42:20,795 INFO [org.keycloak.exportimport.singlefile.SingleFileExportProvider] (ServerService Thread Pool -- 54) Exporting realm 'non-existing' into file /tmp/export/keep-growing-realm.json
11:42:20,804 FATAL [org.keycloak.services] (ServerService Thread Pool -- 54) Error during startup: java.lang.NullPointerException
If you see the No such file or directory
message in the logs make sure that the Dkeycloak.migration.file
property value points to a path that actually exists within the container. The following example error shows what happens if I provide /tmp/wrong/directory/path/keep-growing-realm.json
to the command but Docker actually created a folder according to the mapped volume which points to /tmp/export
:
Exporting realm 'keep-growing' into file /tmp/wrong/directory/path/keep-growing-realm.json
11:50:20,269 FATAL [org.keycloak.services] (ServerService Thread Pool -- 53) Error during startup: java.lang.RuntimeException: Error during export/import: /tmp/wrong/directory/path/keep-growing-realm.json (No such file or directory)
Verify that you actually restarted the container after adding the volume config to the docker-compose.yml
file. Otherwise, the mapped directory won’t be created for you.
There are no errors in logs but you can’t find the exported files on your machine? Verify that the Dkeycloak.migration.file
property value is consistent with the volume mapping. The following example shows what happens if I provide /tmp/keep-growing-realm-test.json
instead of /tmp/export/keep-growing-realm-test.json
:
You can always verify the location of the exported data in the command logs:
11:59:32,192 INFO [org.keycloak.services] (ServerService Thread Pool -- 53) KC-SERVICES0034: Export of realm 'keep-growing' requested.
11:59:32,192 INFO [org.keycloak.exportimport.singlefile.SingleFileExportProvider] (ServerService Thread Pool -- 53) Exporting realm 'keep-growing' into file /tmp/keep-growing-realm-test.json
11:59:32,805 INFO [org.keycloak.services] (ServerService Thread Pool -- 53) KC-SERVICES0035: Export finished successfully
Photo by RODNAE Productions from Pexels
Spring Security allows us to use role-based control to restrict access to API resources. However,…
A custom annotation in Spring Boot tests is an easy and flexible way to provide…
Delegating user management to Keycloak allows us to better focus on meeting the business needs…
Swagger offers various methods to authorize requests to our Keycloak secured API. I'll show you…
Configuring our Spring Boot API to use Keycloak as an authentication and authorization server can…
Keycloak provides simple integration with Spring applications. As a result, we can easily configure our…
View Comments
Hi,
While running command to export in folder, getting below error
/tmp/export/testing-realm-realm.json (Permission denied)
3:41:45,821 INFO [org.hibernate.orm.beans] (ServerService Thread Pool -- 59) HHH10005002: No explicit CDI BeanManager reference was passed to Hibernate, but CDI is available on the Hibernate ClassLoader.
13:41:46,076 INFO [org.hibernate.validator.internal.util.Version] (ServerService Thread Pool -- 59) HV000001: Hibernate Validator 6.0.22.Final
13:41:47,433 INFO [org.hibernate.hql.internal.QueryTranslatorFactoryInitiator] (ServerService Thread Pool -- 59) HHH000397: Using ASTQueryTranslatorFactory
13:41:48,485 INFO [org.keycloak.services] (ServerService Thread Pool -- 59) KC-SERVICES0034: Export of realm 'testing-realm' requested.
13:41:49,063 FATAL [org.keycloak.services] (ServerService Thread Pool -- 59) Error during startup: java.lang.RuntimeException: Error during export/import: /tmp/export/testing-realm-realm.json (Permission denied)
at org.keycloak.keycloak-services@17.0.1//org.keycloak.exportimport.util.ExportImportSessionTask.run(ExportImportSessionTask.java:37)
at org.keycloak.keycloak-server-spi-private@17.0.1//org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction(KeycloakModelUtils.java:239)
at org.keycloak.keycloak-services@17.0.1//org.keycloak.exportimport.util.MultipleStepsExportProvider.exportRealmImpl(MultipleStepsExportProvider.java:74
Kecloack container:
drwxr-xr-x 2 root root 4096 Apr 5 11:13 export
drwxr-xr-x 2 jboss root 4096 Apr 5 13:41 hsperfdata_jboss
drwxr-xr-x 1 root root 4096 Mar 30 13:42 hsperfdata_root
-rwx------ 1 root root 291 Mar 28 09:18 ks-script-c9hd6mir
-rwx------ 1 root root 701 Mar 28 09:18 ks-script-jrnjxyf4
The permission problem seems to be due to the fact that the directory `export` belongs to the user `root`. In my configuration, the owner is `jboss`:
```
drwxrwxr-x 2 jboss 1000 4096 Feb 3 12:52 export
drwxr-xr-x 2 jboss root 4096 Apr 7 20:04 hsperfdata_jboss
drwxr-xr-x 1 root root 4096 Jan 25 15:26 hsperfdata_root
```
The similar exception will be thrown when the volume on your local machine is owned by the `root`.
This is for legacy wildfly servers, that will be deprecated very soon. Making any tutorial in 2022 regarding this is just a clickbait. Prior to v17, there are countless tutorials out there, i.e. dasinko
And the export that wildly based keycloak produces, cannot be consumed by quarkus bases (v17+) keycloak
Especially when going for big migrations I'm thankful for working backups of legacy systems. So thanks a lot for the tutorial <3
I can relate to that, as even though the tutorial above is very organized, it is quite distant if I attempt to follow it with my keycloak v20
In contrary, this tutorial helped me very much and the export was successfully imported in quarkus 19.0.3.
13:00:21,274 FATAL [org.keycloak.services] (ServerService Thread Pool -- 52) Error during startup: org.hibernate.HibernateException: Transaction was rolled back in a different thread!
at org.hibernate@5.3.24.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorTrackingImpl.processAnyDelayedAfterCompletion(SynchronizationCallbackCoordinatorTrackingImpl.java:90)
hello, i got this error when i want to export users from keycloak
13:00:21,274 FATAL [org.keycloak.services] (ServerService Thread Pool -- 52) Error during startup: org.hibernate.HibernateException: Transaction was rolled back in a different thread!
at org.hibernate@5.3.24.Final//org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorTrackingImpl.processAnyDelayedAfterCompletion(SynchronizationCallbackCoordinatorTrackingImpl.java:90)
Hi We are using keycloak.
we are no able to login into admin console and rest is working file
https://ibb.co/PxyHBMV
I am sharing these screen shot.
Hi Marta, great blog! Is it possible to export realm from a running keycloak in container without stopping? As soon as I stop keycloak for export, its container stops also.