From Keycloak to Keycloak.X

23.12.2021 | 14 minutes of reading time

The popular open-source IAM solution Keycloak (see project page ) is undergoing a major technology change. As part of the Keycloak.X efforts , the underlying platform is to be changed from Wildfly/Undertow to Quarkus/Vertx. This platform change has been ongoing for quite a while now and the project recently announced that it is about to release a production grade version of Keycloak.X soon. Once it’s ready, we can use the new Keycloak.X distribution in production.

This announcement was pretty exciting for us as we use Keycloak on a daily basis to implement IAM solutions for our customers. Many customer projects have shown that a classic Wildfly-based Keycloak system poses major challenges for development teams: the configuration is very complex, the resource footprint is relatively high and the server is not optimized for operation in modern cloud environments.

Customers who are eagerly following the latest Keycloak developments regularly ask us what Keycloak.X is, what benefits it provides and when it will be ready for prime time. This is a good opportunity to take a look at Keycloak.X. To explain this, we provide an overview of Keycloak.X, show some of its features, and demonstrate a simple project setup.

The version of Keycloak used throughout this article is 15.1.1.


The Keycloak Server is effectively a large JAX-RS web application deployed in an application server platform that is either Wildfly (Keycloak Community) or JBoss EAP (Red Hat SSO). The web application is served via the embedded Undertow HTTP server. Keycloak.X changes the Wildfly/Undertow platform to Quarkus and Vertx while keeping the JAX-RS application infrastructure. This allows it to keep using the same APIs while leveraging many of the platform features provided by Quarkus, such as flexible configuration, build time optimizations, lower resource consumption as well as faster startup times and a container-optimized runtime.

Keycloak.X umbrella project

Keycloak.X theme is not only about changing the platform of the Keycloak server, but it also incorporates other efforts that have different timelines. One notable example is the new storage model , which will enable Keycloak to support true zero-downtime upgrades, which is currently only possible in rare cases with the current Keycloak storage model.
The new map-based storage model has been in development for quite some time already, but is not yet ready for prime time at the moment. Other topics, like splitting up the monolithic server into smaller, more narrowly focused services, are also part of the long-term Keycloak.X effort.

Faster startup time

One of the noteworthy differences between the classic Wildfly-based Keycloak and Keycloak.X is the startup time. Due to many optimizations that went into the Quarkus platform, the Keycloak Server can now start within a few seconds in contrast to potentially minutes as we’ve observed in some customer projects.

Startup times of a plain Keycloak docker container vs. starting a Keycloak.X .

Lower resource consumption

At the present stage, Keycloak.X already consumes significantly less resources for the same work as Keycloak. For one, the lightweight Quarkus / Vertx runtime has much less overhead for request processing than the Wildfly / Undertow runtime and does not need to carry the application server baggage in the JVM process.

Memory usage of plain Keycloak vs. Keycloak.X Docker Container .

Simpler configuration

The classic Keycloak server supports configuration from multiple sources like system properties, environment variables, properties files and Wildfly-specific configuration files like standalone.xml and standalone-ha.xml. However, each mechanism works differently and cannot be easily combined. Additionally there is no concept to denote different configuration stages. In contrast to that, Keycloak.X uses the Quarkus configuration support which provides a homogenous way to configure values in simple properties files that can be overridden with corresponding system properties or environment variables. Additionally, support for profiles or environment / staging-based configuration is provided from the get-go as well.

Modern libraries and faster upgrade cycles

Keycloak.X leverages the best-of-breed library composition that is provided by the underlying Quarkus platform. This means that we can use much more modern libraries out of the box compared to what used to be available in the Wildfly base installation. This also allows for faster library upgrades for CVE fixes. There were also some major library upgrades, which might require you to update your extensions. For example, the JAX-RS implementation Resteasy 3.x was replaced with its binary incompatible successor Resteasy 4.x.

Will my existing extensions and themes still work?

Short answer: very likely yes.
As the underlying Keycloak API stays the same, you can expect your custom extensions to work just fine. If you integrate with the Wildfly infrastructure or explicitly use Resteasy 3 components, then you might need to adapt your code accordingly or recompile your extension against the new Resteasy version.

Your custom themes will also continue to work.

Getting started with Keycloak.X

This chapter focuses on the usual first steps with Keycloak.X. In contrast to the existing description in Keycloak.X Blogpost from keycloak.org, we use the Keycloak.X Docker image in our examples.

Image version and working directory

Start with defining the Keycloak image and a working directory. In the following, keycloak-x in the current user’s home directory is used. As before, the default Keycloak.X Docker image requires a user with an id=1000 to work properly with Docker volume mounts.

1    KC_HOME=~/keycloak-x
2    KC_IMAGE=quay.io/keycloak/keycloak-x:15.1.1
4    # Create base directory and data folder
5    mkdir -p $KC_HOME/data
7    cd $KC_HOME

Using Keycloak.X in dev mode

The easiest way to start a Keycloak.X instance is by running a Docker container with the following command:

1    # Start in Dev Mode
2    docker run \
3    -it \
4    --rm \
5    --name kcx \
6    -e KEYCLOAK_ADMIN=admin \
8    -p 8080:8080 \
9    -v $PWD/data:/opt/keycloak/data:z \
10    $KC_IMAGE \
11    start-dev

This starts Keycloak.X in development mode with HTTP enabled and theme caches disabled by default.

Keycloak will be ready after a few seconds on http://localhost:8080. Use admin / admin to log into the Admin Console.

As shown above, the Keycloak admin user credentials can be specified via the environment variables KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD.

A noticeable difference is the missing jboss-subfolder when mounting the data-folder.

Another noticeable difference one notices when trying to access the Keycloak admin-console is the missing /auth context-path prefix. The admin console is available at http://localhost:8080/admin instead of http://localhost:8080/auth/admin.
Note that the start-dev parameter is intended for non-productive usage to speed up development time. Without start-dev, kc.sh just prints a usage-hint. By adding --help to start Keycloak, some possible parameters are printed. Note that you can show even more command-line options if you use the --help-all flag instead.

The first recommendation from this usage-hint is to employ the start command for using Keycloak.X in production. If start is used without additional parameters, the startup fails with remarks about missing configuration, but that will be covered later on.

How to configure Keycloak.X

The server configuration is now centralized in a single configuration file named keycloak.properties instead of a keycloak-server.json or standalone.xml/standalone-ha.xml file. In the running Docker container, the file is located at: /opt/keycloak/conf/keycloak.properties and for the current version of the container (15.1.1) the contents look like this:

1    # Default and non-production grade database vendor
2    db=h2-file
3    db.username = sa
4    db.password = keycloak
6    # Insecure requests are disabled by default
7    http.enabled=false
9    # Metrics and healthcheck are disabled by default
10    metrics.enabled=false
12    # Basic settings for running in production. Change accordingly before deploying the server.
13    # Database
14    #%prod.db=postgres
15    #%prod.db.username=keycloak
16    #%prod.db.password=password
17    #%prod.db.url=jdbc:postgresql://localhost/keycloak
18    # Observability
19    #%prod.metrics.enabled=true
20    # HTTP
21    #%prod.spi.hostname.frontend-url=https://localhost:8443
22    #%prod.https.certificate.file=${kc.home.dir}conf/server.crt.pem
23    #%prod.https.certificate.key-file=${kc.home.dir}conf/server.key.pem
24    #%prod.proxy=reencrypt
25    #%prod.hostname=myhostname
27    # Default, and insecure, and non-production grade configuration for the development profile
28    %dev.http.enabled=true
29    %dev.hostname.strict=false
30    %dev.hostname.strict-https=false
31    %dev.cache=local
32    %dev.spi.theme.cache-themes=false
33    %dev.spi.theme.cache-templates=false
34    %dev.spi.theme.static-max-age=-1
36    # The default configuration when running in import or export mode
37    %import_export.http.enabled=true
38    %import_export.hostname.strict=false
39    %import_export.hostname.strict-https=false
40    %import_export.cluster=local
42    # Logging configuration. INFO is the default level for most of the categories
43    #quarkus.log.level = DEBUG
44    quarkus.log.category."org.jboss.resteasy.resteasy_jaxrs.i18n".level=WARN
45    quarkus.log.category."org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup".level=WARN

The properties style configuration file contains one setting per line. Configuration keys can contain prefixes which allow grouping settings with a common name. Quarkus also provides an easy way to express profile-specific configurations. In the example above, the %dev. prefix describes settings that are only applied in the dev profile. Settings without a prefix are considered prod profile settings, if not explicitly marked with another profile prefix.

The current configuration can be printed to the console by running Keycloak with the show-config all command.

1    docker run \
2    -it \
3    --rm \
4    $KC_IMAGE \
5    show-config all

Example output for show-config all looks like this:

1    Current Profile: none
2    Runtime Configuration:
3    kc.config.args =  show-config;;all (KcSysPropConfigSource)
4    kc.db =  h2-file (PropertiesConfigSource[source=keycloak.properties])
5    kc.db.password =  ******* (PropertiesConfigSource[source=keycloak.properties])
6    kc.db.username =  sa (PropertiesConfigSource[source=keycloak.properties])
7    kc.home.dir =  /opt/keycloak/bin/../ (KcSysPropConfigSource)
8    kc.http.enabled =  false (PropertiesConfigSource[source=keycloak.properties])
9    kc.metrics.enabled =  false (PropertiesConfigSource[source=keycloak.properties])
10    kc.show.config =  all (KcSysPropConfigSource)
11    kc.version =  15.1.1 (KcSysPropConfigSource)
12    Profile "dev" Configuration:
13    %dev.kc.cache =  local (PropertiesConfigSource[source=keycloak.properties])
14    %dev.kc.hostname.strict =  false (PropertiesConfigSource[source=keycloak.properties])
15    %dev.kc.hostname.strict-https =  false (PropertiesConfigSource[source=keycloak.properties])
16    %dev.kc.http.enabled =  true (PropertiesConfigSource[source=keycloak.properties])
17    %dev.kc.spi.theme.cache-templates =  false (PropertiesConfigSource[source=keycloak.properties])
18    %dev.kc.spi.theme.cache-themes =  false (PropertiesConfigSource[source=keycloak.properties])
19    %dev.kc.spi.theme.static-max-age =  -1 (PropertiesConfigSource[source=keycloak.properties])
20    Profile "import_export" Configuration:
21    %import_export.kc.cluster =  local (PropertiesConfigSource[source=keycloak.properties])
22    %import_export.kc.hostname.strict =  false (PropertiesConfigSource[source=keycloak.properties])
23    %import_export.kc.hostname.strict-https =  false (PropertiesConfigSource[source=keycloak.properties])
24    %import_export.kc.http.enabled =  true (PropertiesConfigSource[source=keycloak.properties])
25    Quarkus Configuration:
26    quarkus.log.category."org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup".level =  WARN (PropertiesConfigSource[source=keycloak.properties])
27    quarkus.log.category."org.jboss.resteasy.resteasy_jaxrs.i18n".level =  WARN (PropertiesConfigSource[source=keycloak.properties])

Keycloak.X supports the creation of a new and optimized server image based on the configuration options provided by dynamically generating bytecode. Once created, the configuration will be persisted and read during startup without having to process them over again, which improves startup time.

Therefore some configuration options require the build command to be executed in order to actually change a configuration. This applies for options like

  • Change database vendor
  • Enable/disable features
  • Enable/Disable providers or set a default

The Keycloak team recommends running the build command before running the server in production for an optimal runtime experience.

kc.sh build [OPTIONS], e.g.:


1    FROM quay.io/keycloak/keycloak-x:15.1.1
2    RUN /opt/keycloak/bin/kc.sh build  --metrics-enabled=true

Build the Docker image:

1    docker build -t mykeycloakx .
3    docker run \
4    -it \
5    --rm \
6    --name kcx \
7    -e KEYCLOAK_ADMIN=admin \
9    -p 8080:8080 \
10    -v $PWD/data:/opt/jboss/keycloak/data:z \
11    mykeycloakx \
12    start --http-enabled=true

Run the custom Docker image:

1    docker run \
2    -it \
3    --rm \
4    --name kcx \
5    -e KEYCLOAK_ADMIN=admin \
7    -p 8080:8080 \
8    -v $PWD/data:/opt/keycloak/data:z \
9    mykeycloakx \
10    start --http-enabled=true --http-port=8080 --http-host= --hostname=localhost:8080 --hostname-strict-https=false

Note that, due to a bug in Keycloak 15.1.1, we need to specify the hostname together with port for the --hostname parameter. In later versions of Keycloak you just need to configure the plain hostname.

How to adjust the context-path

As mentioned above, Keycloak.X uses an empty context-path AKA relative-path setting. If you want to use the old context path /auth, which is used in Wildfly-based Keycloak distributions, you need to provide the build option –-http-relative-path=auth.
Alternatively you can also use the environment variable KC_HTTP_RELATIVE_PATH or the Keycloak configuration setting kc.http.relative-path.
Adjusting the context-path to the old value might make it easier to migrate to Keycloak.X in existing Keycloak environments.

The above show-config all command reveals this as one of many settings.

Besides the configuration via file, settings can be provided via command-line parameter or environment variables. The Keycloak.X documentation regarding configuration options can be found here .

1    # Start in Dev Mode with custom context-path
2    docker run \
3    -it \
4    --rm \
5    --name kcx \
6    -e KEYCLOAK_ADMIN=admin \
8    -p 8080:8080 \
9    -v $PWD/data:/opt/keycloak/data:z \
10    $KC_IMAGE \
11    start-dev --http-relative-path=auth

Now Keycloak should be accessible on http://localhost:8080/auth.

How to enable TLS

As described in the previous section, the dev-profile works out of the box using HTTP and a local h2 database setting preset. To move on to a more production-like setup, it is strongly recommended to use HTTPS. To configure this, the following steps are required.


For a local setup, put the hostname for accessing Keycloak in the hosts file of your system. On Linux or OSX the hosts file can be found in /etc/hosts and C:\Windows\System32\Drivers\etc\hosts on Windows.

For this example we want to use the domain name id.keycloak.test, hence we configure the following hosts entry:

1 id.keycloak.test

Self-signed certificate

In order to use HTTPS, we need to create a certificate and private key. For testing purposes we use a self-signed certificate. Create and install self-signed certificates for that hostname, e.g. with a tool like mkcert by running the following command:

mkcert -install id.keycloak.test

This will generate two files: The certificate is at "./id.keycloak.test+1.pem" and the key at "./id.keycloak.test+1-key.pem".

Running with prod profile

The following command shows how to run with HTTPS enabled using the created certificates.

1    docker run \
2     -it \
3     --rm \
4     --name kcx \
5     -e KEYCLOAK_ADMIN=admin \
7     -e KC_DB_USERNAME=sa \
8     -e KC_DB_PASSWORD=keycloak \
9     -p 8080:8080 \
10     -p 8443:8443 \
11     -v $PWD/data:/opt/keycloak/data:z \
12     -v $PWD/id.keycloak.test+1.pem:/etc/x509/https/tls.crt:z \
13     -v $PWD/id.keycloak.test+1-key.pem:/etc/x509/https/tls.key:z \
14     $KC_IMAGE \
15     start \
16     --auto-build \
17     --hostname=id.keycloak.test:8443 \
18     --https-certificate-file=/etc/x509/https/tls.crt \
19     --https-certificate-key-file=/etc/x509/https/tls.key

Note that the Keycloak.X version 15.1.0 introduced the --auto-build feature, which dynamically detects new extensions in the /provider and dynamically generates build-time configuration. This comes at the expense of additional startup time. If you want a fast startup, then you can run the command

    bin/kc.sh build

to index the provided extensions as shown in the Dockerfile example above. After running the build command, you can start the server via kc.sh start without the --auto-build flag.

Default database settings and verbosity are also added in comparison to the previous start command to get rid of the dev-profile.

The output should show something like:

1   <TIMESTAMP> INFO  [io.quarkus] (main) Keycloak 15.1.1 on JVM (powered by Quarkus 2.5.1.Final) started in 7.014s. Listening on:
2   <TIMESTAMP> INFO  [io.quarkus] (main) Profile prod activated.

Keycloak should be available at: https://id.keycloak.test:8443/ without complaints about untrusted certificates. Note that as mentioned above, the hostname parameter currently needs to contain the hostname+port, however in later Keycloak versions this will change.

How to add custom themes

Keycloak.X still supports folder and JAR-based custom themes. Folder-based custom themes must be placed in the /opt/keycloak/themes directory. Custom themes provided as .jar-files need to be copied to the /opt/keycloak/providers folder.

Note that nothing has changed in regards to the theme engine itself.

For theme development the dev-profile conveniently disables caching as most Keycloak theme developers might already have seen in the configuration section:

1    %dev.spi.theme.cache-themes=false
2    %dev.spi.theme.cache-templates=false
3    %dev.spi.theme.static-max-age=-1

How to add custom extensions

Custom extensions must be placed in /opt/keycloak/providers as .jar-files.

Regarding extensions and its SPIs, no change is expected comparing Keycloak and Keycloak.X. Needless to say, the seamless migration of extensions depends on the written code in areas beside Keycloak’s SPIs too. When we migrated our project example and extensions playground, we found issues related to Resteasy version 3.x vs 4.x, but nothing serious. After a small refactoring everything works for both worlds. We would like to hear the story about your migration!

How to remote-debug Keycloak.X

To enable remote debugging for the Keycloak.X container, you need to configure the DEBUG_PORT environment variable. To restrict the remote debugging to a specific network interface, one can use DEBUG_PORT=''.

An example execution with enabled debugging that allows for connecting a remote debugger via multiple endpoints looks like this:

1    docker run \
2    -it \
3    --rm \
4    --name kcx \
5    -e KEYCLOAK_ADMIN=admin \
7    -e KC_DB_USERNAME=sa \
8    -e KC_DB_PASSWORD=keycloak \
9    -e DEBUG_PORT='*:8787' \
10    -p 8443:8443 \
11    -p 8787:8787 \
12    -v $PWD/data:/opt/keycloak/data:z \
13    -v $PWD/id.keycloak.test+1.pem:/etc/x509/https/tls.crt:z \
14    -v $PWD/id.keycloak.test+1-key.pem:/etc/x509/https/tls.key:z \
15    $KC_IMAGE \
16    start \
17    --auto-build \
18    --hostname=id.keycloak.test:8443 \
19    --https-certificate-file=/etc/x509/https/tls.crt \
20    --https-certificate-key-file=/etc/x509/https/tls.key \
21    --debug

Once the container has started, one can attach the debugger to the JVM process via localhost:8787 with the IDE of your choice. Note that the classpath for the debugger must contain the relevant JARs to be able to set breakpoints.

How to add JVM Options

Keycloak.X accepts JVM settings via JAVA_OPTS and JAVA_TOOLING_OPTS.

If nothing is given the default JAVA_OPTS contain:

1    JAVA_OPTS="-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true"

If JAVA_OPTS is given as a parameter, the default is overwritten. This hint is also printed to the log during startup. Note that you can use the JAVA_TOOLING_OPTS to configure additional JVM settings like JMX configuration etc. Note that since Keycloak 16.0.0 the environment variable JAVA_OPTS_APPEND can be used to append custom JVM options without overriding default settings.

For production, a lot of things might be worth considering which is beyond the scope of this article.

How to use the JMX with Keycloak.X

Since Keycloak.X is a normal Java application, we can also use classic tools like VisualVM or Java Mission Control to manage the application. To do so, we need to add the following JAVA_TOOLING_OPTS together with exposed port 8790 to our container invocation.

1    docker run \
2     -it \
3     --rm \
4     --name kcx \
5     -e KEYCLOAK_ADMIN=admin \
7     -e KC_DB_USERNAME=sa \
8     -e KC_DB_PASSWORD=keycloak \
9     -e JAVA_TOOL_OPTIONS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8790 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dquarkus-log-max-startup-records=10000" \
10     -p 8443:8443 \
11     -p 8790:8790 \
12     -v $PWD/data:/opt/keycloak/data:z \
13     -v $PWD/id.keycloak.test+1.pem:/etc/x509/https/tls.crt:z \
14     -v $PWD/id.keycloak.test+1-key.pem:/etc/x509/https/tls.key:z \
15     $KC_IMAGE \
16     start \
17     --auto-build \
18     --hostname=id.keycloak.test:8443 \
19     --https-certificate-file=/etc/x509/https/tls.crt \
20     --https-certificate-key-file=/etc/x509/https/tls.key

Once the container is started, one can start e.g. VisualVM or Java Mission Control and connect via JMX to localhost:8790 and explore the runtime of Keycloak.X as usual.


This blog post highlighted a few new features of the Quarkus-based Keycloak.X server distribution and gave examples of configuring Keycloak.X for various purposes.
In a recent post in the Keycloak team blog, a concrete Roadmap for Keycloak.X was announced that we and our customers read with great interest. The roadmap is challenging, but in our eyes a big step forward to mitigate or review problematic areas of the current Keycloak server. In our opinion, Keycloak.X still has some rough edges, but can already be used for testing in dev environments. As we have shown in the keycloak-project-template , migrating a modest-size custom Keycloak project can be done with little effort. We are looking forward to the upcoming stable releases of Keycloak.X and will continue investigating. Our next blog post will probably focus on a production-grade Keycloak.X setup with clustering.

share post




More articles in this subject area\n

Discover exciting further topics and let the codecentric world inspire you.


Gemeinsam bessere Projekte umsetzen

Wir helfen Deinem Unternehmen

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.