DEV Community

Cover image for Optimized Keycloak build
Mohammed Ammer
Mohammed Ammer

Posted on

Optimized Keycloak build

Keycloak provides a documentation on how is to configure Keycloak the proper way in Kubernetes. I wouldn't repeat the documentation so please make sure you go through it.

The most important part when said:

We recommend optimizing Keycloak to provide faster startup and better memory consumption before deploying Keycloak in a production environment. This section describes how to apply Keycloak optimizations for the best performance and runtime behavior - Optimize the startup

For that to happen, let us write our Dockerfile

The Dockerfile

Let us have a look to the entire Dockerfile before we go into each Dockefile layer.

ARG KC_VERSION

FROM quay.io/keycloak/keycloak:${KC_VERSION} AS builder

ARG KC_HEALTH_ENABLED=true
ARG KC_METRICS_ENABLED=true
ARG KC_DB=postgres
ARG KC_FEATURES
ARG KC_METRICS_SPI_VERSION
ARG KC_HOSTNAME_PATH=keycloak
ARG KC_HTTP_RELATIVE_PATH=/keycloak

ENV KC_VERSION=${KC_VERSION}
ENV KC_HEALTH_ENABLED=${KC_HEALTH_ENABLED}
ENV KC_DB=${KC_DB}
ENV KC_HOSTNAME_PATH=${KC_HOSTNAME_PATH}
ENV KC_HTTP_RELATIVE_PATH=${KC_HTTP_RELATIVE_PATH}
ENV KC_FEATURES=${KC_FEATURES}

ADD --chown=keycloak:keycloak https://github.com/aerogear/keycloak-metrics-spi/releases/download/${KC_METRICS_SPI_VERSION}/keycloak-metrics-spi-${KC_METRICS_SPI_VERSION}.jar /opt/keycloak/providers/keycloak-metrics-spi-${KC_METRICS_SPI_VERSION}.jar

RUN /opt/keycloak/bin/kc.sh build --cache-stack=kubernetes

# Installing additional RPM packages https://www.keycloak.org/server/containers
FROM registry.access.redhat.com/ubi9:9.3 AS ubi-micro-build
RUN mkdir -p /mnt/rootfs
RUN dnf install -y curl-7.76.1 --installroot /mnt/rootfs  --releasever 9 --setopt install_weak_deps=false --nodocs && \
    dnf --installroot /mnt/rootfs clean all && \
    rpm --root /mnt/rootfs -e --nodeps setup

FROM quay.io/keycloak/keycloak:${KC_VERSION}

COPY --from=builder /opt/keycloak/ /opt/keycloak/
COPY --from=ubi-micro-build /mnt/rootfs /
COPY ./entrypoint.sh /deployments/entrypoint.sh

ENTRYPOINT ["/deployments/entrypoint.sh"]

Enter fullscreen mode Exit fullscreen mode

Build Keycloak

For maintainability, parameterize the Keycloak version so it is easier from the build tool (CI/CD) to upgrade Keycloak without touching the Dockerfile. Below is the first stage in our multi-stage Dockerfile defined as builder

ARG KC_VERSION

FROM quay.io/keycloak/keycloak:${KC_VERSION} AS builder
Enter fullscreen mode Exit fullscreen mode

Arguments

Here is the Dockerfile arguments.

ARG KC_HEALTH_ENABLED=true
ARG KC_METRICS_ENABLED=true
ARG KC_DB=postgres
ARG KC_FEATURES
ARG KC_METRICS_SPI_VERSION
ARG KC_HOSTNAME_PATH=keycloak
ARG KC_HTTP_RELATIVE_PATH=/keycloak
Enter fullscreen mode Exit fullscreen mode
  • KC_HEALTH_ENABLED and KC_METRICS_ENABLED: Both are important for monitoring Keycloak. I enabled them by default.
  • KC_DB: For the target database provider. I consider postgres database.
  • KC_FEATURES: To control the enabling features in Keycloak from CI/CD.
  • KC_METRICS_SPI_VERSION: If KC_METRICS_ENABLED is true, the target version of the Keycloak Metrics SPI. Keycloak Metrics SPI is not an official metrics from Keycloak but a well known SPI. It worth to check and decide about it.
  • KC_HOSTNAME_PATH and KC_HTTP_RELATIVE_PATH are required to be added by the same name if you choose different context path for Keycloak than the default /. In my case, I choose /keycloak as context path.

Prepare the environment variables

ENV KC_VERSION=${KC_VERSION}
ENV KC_HEALTH_ENABLED=${KC_HEALTH_ENABLED}
ENV KC_DB=${KC_DB}
ENV KC_HOSTNAME_PATH=${KC_HOSTNAME_PATH}
ENV KC_HTTP_RELATIVE_PATH=${KC_HTTP_RELATIVE_PATH}
ENV KC_FEATURES=${KC_FEATURES}
Enter fullscreen mode Exit fullscreen mode

Keycloak uses the environment variables during the build to read from and act based on it. In the above snippet, the required environment variables for our build added.

Prepare the SPIs

ADD --chown=keycloak:keycloak https://github.com/aerogear/keycloak-metrics-spi/releases/download/${KC_METRICS_SPI_VERSION}/keycloak-metrics-spi-${KC_METRICS_SPI_VERSION}.jar /opt/keycloak/providers/keycloak-metrics-spi-${KC_METRICS_SPI_VERSION}.jar
Enter fullscreen mode Exit fullscreen mode

A example build step that downloads a JAR file from a URL and adds it to the providers directory

Build Keycloak

RUN /opt/keycloak/bin/kc.sh build --cache-stack=kubernetes
Enter fullscreen mode Exit fullscreen mode

This is the most important step where we build Keycloak based on the environment variables we provided in the build step and cache stack as kubernetes.

Install additional RPM packages

# Installing additional RPM packages https://www.keycloak.org/server/containers
FROM registry.access.redhat.com/ubi9:9.3 AS ubi-micro-build
RUN mkdir -p /mnt/rootfs
RUN dnf install -y curl-7.76.1 --installroot /mnt/rootfs  --releasever 9 --setopt install_weak_deps=false --nodocs && \
    dnf --installroot /mnt/rootfs clean all && \
    rpm --root /mnt/rootfs -e --nodeps setup
Enter fullscreen mode Exit fullscreen mode

This step is not necessary, although if you need to have additional RPM packages in the container, you can install it as shown in the above example for installing curl.

Final image.

FROM quay.io/keycloak/keycloak:${KC_VERSION}

COPY --from=builder /opt/keycloak/ /opt/keycloak/
COPY --from=ubi-micro-build /mnt/rootfs /
COPY ./entrypoint.sh /deployments/entrypoint.sh
Enter fullscreen mode Exit fullscreen mode

The final stage is our image where Keycloak build files are pre-installed and ready to start up.

  • From the builder stage, we copy the Keycloak build
  • From the ubi-micro-build stage, we copy the installed packages
  • entrypoint.sh is our custom entrypoint file to start Keycloak. In its basic form can be as simple as below:
exec /opt/keycloak/bin/kc.sh start --optimized
Enter fullscreen mode Exit fullscreen mode

I prefer to have the entrypoint in separate file so later I have the flexibility to customize it without modifying the Dockerfile. Indeed I use additional scripts in the entrypoint. For instance, integrate Datadog with Keycloak. I can write a separate article to describe it ;)


Keycloak is an identity provider which provides the front-end for the users same as admins. Someone would think to narrow the exposed part of Keycloak to only what necessary public and keep everything else to be accessed only through VPN / Internal network.
In the next blog post, I describe how you can separate Keycloak public and private APIs/URLs for an enhanced security.

I hope you find it useful.

Top comments (0)