Deploy Keycloak in Dokku

Intro

Dokku is an extensible, open source Platform as a Service that runs on a single server of your choice. Dokku supports building apps on the fly from a git push via either Dockerfile or by auto-detecting the language with Buildpacks, and then starts containers based on your built image.

https://dokku.com/docs/getting-started/installation/

Dokku is an onprem software alternative to heroku.com or clever-cloud.com.

In other words : you git push your application, then let the magic happen.

We will deploy a Keycloak, ready for production, on Dokku.

Sources available at : https://github.com/please-openit/dokku-keycloak

Install Dokku

Refer to the documentation

In our case, we installed it on a cloud instance from OVHCloud :

wget -NP . https://dokku.com/install/v0.35.11/bootstrap.sh
sudo DOKKU_TAG=v0.35.11 bash bootstrap.sh

Set a domain

To have all features, we need a custom domain with a wildcard redirect to our newly created server :

alt text

dokku domains:set-global dokku.please-open.it
dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

Create a database

Keycloak needs a postgres database.

https://github.com/dokku/dokku-postgres

root@d2-4-gra11:/home/ubuntu# dokku postgres:create kc_postgres -m 512 
       Waiting for container to be ready
       Creating container database
       Securing connection to database
=====> Postgres container created: kc_postgres
=====> kc_postgres postgres service information
       Config dir:          /var/lib/dokku/services/postgres/kc_postgres/data
       Config options:                               
       Data dir:            /var/lib/dokku/services/postgres/kc_postgres/data
       Dsn:                 postgres://postgres:46e3fa184970ace22a305b083a37e610@dokku-postgres-kc-postgres:5432/kc_postgres
       Exposed ports:       -                        
       Id:                  340b8f6b97db9008bb66489b51ff7afeaccb2cde730c59a262cf822f51b9aaf7
       Internal ip:         172.17.0.2               
       Initial network:                              
       Links:               -                        
       Post create network:                          
       Post start network:                           
       Service root:        /var/lib/dokku/services/postgres/kc_postgres
       Status:              running                  
       Version:             postgres:17.2            

In order to get the connection URI (with username and password) :

root@d2-4-gra11:/home/ubuntu# dokku postgres:info kc_postgres
=====> kc_postgres postgres service information
       Config dir:          /var/lib/dokku/services/postgres/kc_postgres/data
       Config options:                               
       Data dir:            /var/lib/dokku/services/postgres/kc_postgres/data
       Dsn:                 postgres://postgres:46e3fa184970ace22a305b083a37e610@dokku-postgres-kc-postgres:5432/kc_postgres
       Exposed ports:       -                        
       Id:                  340b8f6b97db9008bb66489b51ff7afeaccb2cde730c59a262cf822f51b9aaf7
       Internal ip:         172.17.0.2               
       Initial network:                              
       Links:               -                        
       Post create network:                          
       Post start network:                           
       Service root:        /var/lib/dokku/services/postgres/kc_postgres
       Status:              running                  
       Version:             postgres:17.2            

Backups

You cannot imagine going to production without a decent backup of your database. Out of the box, our postgres database is not backuped. Dokku has a command line for exporting a database :

https://dokku.com/docs~v0.35.11/advanced-usage/backup-recovery/

dokku postgres:export [db_name] > [db_name].dump

Then, by using a simple cron task we enabled a daily backup.

Keycloak App

We will run Keycloak as a Java application, not in a container.

Create the app

root@d2-4-gra11:/home/ubuntu# dokku apps:create keycloak
-----> Creating keycloak...
-----> Global server virtual host not set, disabling app vhost...

We have to link our newly created application to our postgres database.

Command line

From the command line:

root@d2-4-gra11:/home/ubuntu# dokku postgres:link kc_postgres keycloak
-----> Setting config vars
       DATABASE_URL:  postgres://postgres:46e3fa184970ace22a305b083a37e610@dokku-postgres-kc-postgres:5432/kc_postgres
-----> Restarting app keycloak
 !     App image (dokku/keycloak:latest) not found

From config file

Also directly from the config file we will push later:

  • app.json
"addons": [
    "kc_postgres"
  ],

Domain and LetsEncrypt

Keycloak needs its own domain name, with an HTTPS configuration (we use letsencrypt) :

dokku letsencrypt:set keycloak email contact@please-open.it
dokku domains:add keycloak keycloak.dokku.please-open.it
root@d2-4-gra11:/home/ubuntu# dokku letsencrypt:enable keycloak
=====> Enabling letsencrypt for keycloak
-----> Enabling ACME proxy for keycloak...
-----> Getting letsencrypt certificate for keycloak via HTTP-01
        - Domain 'keycloak.dokku.please-open.it'
2024/12/03 08:59:16 [INFO] [keycloak.dokku.please-open.it] acme: Obtaining bundled SAN certificate
2024/12/03 08:59:16 [INFO] [keycloak.dokku.please-open.it] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz/2091439257/439213282697
2024/12/03 08:59:16 [INFO] [keycloak.dokku.please-open.it] acme: Could not find solver for: tls-alpn-01
2024/12/03 08:59:16 [INFO] [keycloak.dokku.please-open.it] acme: use http-01 solver
2024/12/03 08:59:16 [INFO] [keycloak.dokku.please-open.it] acme: Trying to solve HTTP-01
2024/12/03 08:59:40 [INFO] [keycloak.dokku.please-open.it] The server validated our request
2024/12/03 08:59:40 [INFO] [keycloak.dokku.please-open.it] acme: Validations succeeded; requesting certificates
2024/12/03 08:59:40 [INFO] [keycloak.dokku.please-open.it] Server responded with a certificate.
-----> Certificate retrieved successfully.
-----> Installing let's encrypt certificates
-----> Unsetting DOKKU_PROXY_PORT
-----> Configuring keycloak.dokku.please-open.it...(using built-in template)
-----> Creating https nginx.conf
       Enabling HSTS (using built-in template)
       Reloading nginx
-----> Ensuring network configuration is in sync for keycloak
-----> Configuring keycloak.dokku.please-open.it...(using built-in template)
-----> Creating https nginx.conf
       Enabling HSTS (using built-in template)
       Reloading nginx
-----> Disabling ACME proxy for keycloak...
-----> Done

Ok, the proxy configuration with a letsencrypt certificate is up and running.

Push the code

Add the remote repository to our local environment development:

git remote add dokku dokku@dokku.please-open.it:keycloak

Reminder : how to deploy Keycloak

https://www.keycloak.org/server/configuration

2 phases :

  • build
  • start

Mostly, “start” phase includes the “build”. In this case, we will make those 2 phases separately.

Java app

To deploy a java application, we need a buildpack : https://dokku.com/docs/deployment/builders/herokuish-buildpacks/

In a .env file, we declare the buildpack :

export BUILDPACK_URL=https://github.com/heroku/heroku-buildpack-java

And, in system.properties our java version :

java.runtime.version=21

In our app.json, we define :

  • the name and description
  • the “build.sh” script which is run before deployment
  • link to postgres database
{
  "name": "Keycloak",
  "description": "Keycloak SSO.",
  "keywords": [
    "quarkus",
    "keycloak"
  ],
  "scripts": {
    "dokku": {
      "predeploy": "./build.sh"
    }
  },
  "addons": [
    "kc_postgres"
  ]
}

And, a Procfile for overwriting the launch command

web: ./start.sh

Our Java application needs a pom.xml file, for now an empty file will make the work.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>it.please-open</groupId>
   <artifactId>sso</artifactId>
   <packaging>jar</packaging>
   <version>1.0-SNAPSHOT</version>
   <name>keycloak</name>
   <url>http://maven.apache.org</url>
   <properties>
      <java.version>21</java.version>
   </properties>
</project>

Environment variables for Keycloak

Keycloak needs a set of environment variables. So in our .env file :

export KC_VERSION=26.0.6
export KC_DB=postgres
export KC_DB_USERNAME=postgres
export KC_DB_PASSWORD=46e3fa184970ace22a305b083a37e610
export KC_DB_URL_DATABASE=kc_postgres
export KC_DB_URL_PORT=5432
export KC_DB_URL_HOST=dokku-postgres-kc-postgres
export KC_PROXY_HEADERS=xforwarded
export KC_HTTP_ENABLED=true
export KC_HTTP_PORT=5000
export KEYCLOAK_ADMIN=admin
export KEYCLOAK_ADMIN_PASSWORD=password
export KC_HEALTH_ENABLED=true
export KC_HOSTNAME=keycloak.dokku.please-open.it

Build Keycloak

A build.sh file has been defined previously as a “pre deploy script” :

#!/usr/bin/env bash
source .env
echo "Downloading Keycloak $KC_VERSION..."
wget -q https://github.com/keycloak/keycloak/releases/download/$KC_VERSION/keycloak-$KC_VERSION.zip
echo "Unzipping Keycloak $KC_VERSION..."
unzip -q keycloak-$KC_VERSION.zip
mv keycloak-$KC_VERSION keycloak
rm keycloak-$KC_VERSION.zip
echo "Keycloak $KC_VERSION downloaded and unzipped successfuly"

keycloak/bin/kc.sh build

Then, start.sh :

#!/bin/bash
source .env
keycloak/bin/kc.sh start --optimized

Healthcheck

Keycloak has its own health check mechanism :

https://www.keycloak.org/server/health

So, in app.json we add this endpoint :

  "healthchecks": {
    "web": [
        {
            "type":        "startup",
            "name":        "web check",
            "description": "Checking if the app responds to the /health/ready endpoint",
            "path":        "/health/ready",
            "attempts": 15,
            "port": 9000
        }
    ]
  }

Deployment : git push

We are ready :

mathieupassenaud@device-106 keycloak-test % git push
Enumerating objects: 16, done.
Counting objects: 100% (16/16), done.
Delta compression using up to 8 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (9/9), 1.53 KiB | 1.53 MiB/s, done.
Total 9 (delta 2), reused 0 (delta 0), pack-reused 0
-----> Cleaning up...
-----> Building keycloak from herokuish
-----> Adding BUILD_ENV to build environment...
       BUILD_ENV added successfully
-----> Fetching custom buildpack
-----> Java app detected
-----> Installing OpenJDK 21... done
-----> Installing Maven 3.9.4... done
-----> Executing Maven
       $ mvn -DskipTests clean dependency:list install
       [INFO] Scanning for projects...
       [INFO]
       [INFO] -------------------------< it.please-open:sso >-------------------------
       [INFO] Building keycloak 1.0-SNAPSHOT
       [INFO]   from pom.xml
       [INFO] --------------------------------[ jar ]---------------------------------
       [INFO]
       [INFO] --- clean:3.2.0:clean (default-clean) @ sso ---
       [INFO]
       [INFO] --- dependency:3.6.0:list (default-cli) @ sso ---
       [INFO]
       [INFO] --- resources:3.3.1:resources (default-resources) @ sso ---
       [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
       [INFO] skip non existing resourceDirectory /tmp/build/src/main/resources
       [INFO]
       [INFO] --- compiler:3.11.0:compile (default-compile) @ sso ---
       [INFO] No sources to compile
       [INFO]
       [INFO] --- resources:3.3.1:testResources (default-testResources) @ sso ---
       [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
       [INFO] skip non existing resourceDirectory /tmp/build/src/test/resources
       [INFO]
       [INFO] --- compiler:3.11.0:testCompile (default-testCompile) @ sso ---
       [INFO] No sources to compile
       [INFO]
       [INFO] --- surefire:3.1.2:test (default-test) @ sso ---
       [INFO] Tests are skipped.
       [INFO]
       [INFO] --- jar:3.3.0:jar (default-jar) @ sso ---
       [WARNING] JAR will be empty - no content was marked for inclusion!
       [INFO] Building jar: /tmp/build/target/sso-1.0-SNAPSHOT.jar
       [INFO]
       [INFO] --- install:3.1.1:install (default-install) @ sso ---
       [INFO] Installing /tmp/build/pom.xml to /cache/.m2/repository/it/please-open/sso/1.0-SNAPSHOT/sso-1.0-SNAPSHOT.pom
       [INFO] Installing /tmp/build/target/sso-1.0-SNAPSHOT.jar to /cache/.m2/repository/it/please-open/sso/1.0-SNAPSHOT/sso-1.0-SNAPSHOT.jar
       [INFO] ------------------------------------------------------------------------
       [INFO] BUILD SUCCESS
       [INFO] ------------------------------------------------------------------------
       [INFO] Total time:  2.948 s
       [INFO] Finished at: 2024-12-03T09:43:55Z
       [INFO] ------------------------------------------------------------------------
-----> Discovering process types
       Procfile declares types -> web
-----> Releasing keycloak...
-----> Checking for predeploy task
       Executing predeploy task from app.json: ./build.sh
=====> Start of keycloak predeploy task (f13a01ee2) output
remote:  !     Picked up JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=80.0 -Dfile.encoding=UTF-8 
       Downloading Keycloak 26.0.6...
       Unzipping Keycloak 26.0.6...
       Keycloak 26.0.6 downloaded and unzipped successfuly
       JAVA_OPTS already set in environment; overriding default settings
       Updating the configuration and installing your custom providers, if any. Please wait.
       2024-12-03 09:44:11,897 WARN  [org.key.qua.run.cli.Picocli] (main) The following run time options were found, but will be ignored during build time: kc.db-url-host, kc.db-url-database, kc.db-url-port, kc.db-username, kc.db-password, kc.hostname, kc.http-enabled, kc.http-port, kc.proxy-headers
       2024-12-03 09:44:29,043 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 15459ms
       Server configuration updated and persisted. Run the following command to review the configuration:
        kc.sh show-config
=====> End of keycloak predeploy task (f13a01ee2) output
-----> Checking for release task
       No release task found, skipping
=====> Processing deployment checks
-----> Deploying keycloak via the docker-local scheduler...
-----> Deploying web (count=1)
       Attempting pre-flight checks (web.1)
-----> Executing 2 healthchecks
       Running healthcheck name='web check' delay=0 path='/health/ready' retries=14 timeout=5 type='path'
       Running healthcheck name='port listening check' attempts=3 port=5000 retries=2 timeout=5 type='listening' wait=5
remote: 2024/12/03 09:44:48.833004 WARN RESTY Get "http://172.17.0.4:9000/health/ready": dial tcp 172.17.0.4:9000: connect: connection refused, Attempt 1
       Healthcheck succeeded name='port listening check'
remote: 2024/12/03 09:44:53.835066 WARN RESTY Get "http://172.17.0.4:9000/health/ready": dial tcp 172.17.0.4:9000: connect: connection refused, Attempt 2
remote: 2024/12/03 09:44:58.839618 WARN RESTY Get "http://172.17.0.4:9000/health/ready": dial tcp 172.17.0.4:9000: connect: connection refused, Attempt 3
       Healthcheck succeeded name='web check'
       All checks successful (web.1)
=====> Start of keycloak container output (4b80a9625671 web.1)
       Setting JAVA_TOOL_OPTIONS defaults based on dyno size. Custom settings will override them.
       JAVA_OPTS already set in environment; overriding default settings
       Picked up JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=80.0 -Dfile.encoding=UTF-8 
       2024-12-03 09:44:54,228 INFO  [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) Starting Infinispan embedded cache manager
       2024-12-03 09:44:55,436 INFO  [org.infinispan.CONTAINER] (Thread-5) ISPN000556: Starting user marshaller 'org.infinispan.commons.marshall.ImmutableProtoStreamMarshaller'
       2024-12-03 09:44:56,159 WARN  [org.jgroups.stack.Configurator] (Thread-5) JGRP000014: ThreadPool.thread_dumps_threshold has been deprecated: ignored
       2024-12-03 09:44:56,202 INFO  [org.infinispan.CLUSTER] (Thread-5) ISPN000078: Starting JGroups channel `ISPN` with stack `udp`
       2024-12-03 09:44:56,210 INFO  [org.jgroups.JChannel] (Thread-5) local_addr: 019f4d1c-fc71-49ca-a183-d34e860fd296, name: 4b80a9625671-17280
       2024-12-03 09:44:56,237 WARN  [org.jgroups.protocols.UDP] (Thread-5) JGRP000015: the send buffer of socket MulticastSocket was set to 1MB, but the OS only allocated 212.99KB
       2024-12-03 09:44:56,239 WARN  [org.jgroups.protocols.UDP] (Thread-5) JGRP000015: the receive buffer of socket MulticastSocket was set to 20MB, but the OS only allocated 212.99KB
       2024-12-03 09:44:56,240 WARN  [org.jgroups.protocols.UDP] (Thread-5) JGRP000015: the send buffer of socket MulticastSocket was set to 1MB, but the OS only allocated 212.99KB
       2024-12-03 09:44:56,241 WARN  [org.jgroups.protocols.UDP] (Thread-5) JGRP000015: the receive buffer of socket MulticastSocket was set to 25MB, but the OS only allocated 212.99KB
       2024-12-03 09:44:56,283 INFO  [org.jgroups.protocols.FD_SOCK2] (Thread-5) server listening on *.29705
       2024-12-03 09:44:58,298 INFO  [org.jgroups.protocols.pbcast.GMS] (Thread-5) 4b80a9625671-17280: no members discovered after 2006 ms: creating cluster as coordinator
       2024-12-03 09:44:58,314 INFO  [org.infinispan.CLUSTER] (Thread-5) ISPN000094: Received new cluster view for channel ISPN: [4b80a9625671-17280|0] (1) [4b80a9625671-17280]
       2024-12-03 09:44:58,477 INFO  [org.infinispan.CLUSTER] (Thread-5) ISPN000079: Channel `ISPN` local address is `4b80a9625671-17280`, physical addresses are `[172.17.0.4:45241]`
       2024-12-03 09:44:59,387 INFO  [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: 4b80a9625671-17280, Site name: null
       2024-12-03 09:44:59,397 INFO  [org.keycloak.broker.provider.AbstractIdentityProviderMapper] (main) Registering class org.keycloak.broker.provider.mappersync.ConfigSyncEventListener
       2024-12-03 09:45:01,146 WARN  [io.agroal.pool] (main) Datasource '<default>': JDBC resources leaked: 3 ResultSet(s) and 0 Statement(s)
       2024-12-03 09:45:01,413 INFO  [io.quarkus] (main) Keycloak 26.0.6 on JVM (powered by Quarkus 3.15.1) started in 14.418s. Listening on: http://0.0.0.0:5000. Management interface listening on http://0.0.0.0:9000.
       2024-12-03 09:45:01,415 INFO  [io.quarkus] (main) Profile prod activated. 
       2024-12-03 09:45:01,416 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-postgresql, keycloak, micrometer, narayana-jta, opentelemetry, reactive-routes, rest, rest-jackson, smallrye-context-propagation, smallrye-health, vertx]
=====> End of keycloak container output (4b80a9625671 web.1)
       Scheduling old container shutdown in 60 seconds (web.1)
=====> Triggering early nginx proxy rebuild
-----> Ensuring network configuration is in sync for keycloak
-----> Configuring keycloak.dokku.please-open.it...(using built-in template)
-----> Creating https nginx.conf
       Enabling HSTS (using built-in template)
       Reloading nginx
-----> Running post-deploy
-----> Ensuring network configuration is in sync for keycloak
-----> Configuring keycloak.dokku.please-open.it...(using built-in template)
-----> Creating https nginx.conf
       Enabling HSTS (using built-in template)
       Reloading nginx
-----> Renaming containers
       Found previous container(s) (fad541a81f0a) named keycloak.web.1
       Renaming container (fad541a81f0a) keycloak.web.1 to keycloak.web.1.1733219119
       Renaming container keycloak.web.1.upcoming-16414 (4b80a9625671) to keycloak.web.1
-----> Checking for postdeploy task
       No postdeploy task found, skipping
-----> Shutting down old containers in 60 seconds
=====> Application deployed:
       http://keycloak.dokku.please-open.it
       https://keycloak.dokku.please-open.it

To 91.134.36.169:keycloak
 + 5753454...f04f414 main -> main

and boom ! here we are, Keycloak is deployed and ready to use :

alt text

Clustering

Custom ispn.xml

Infinispan is set up with a default jgroups configuration that uses udp-multicast as discovery protocol : https://infinispan.org/docs/stable/titles/configuring/configuring.html#clustered-caches

We prefer JDBC_ping configuration … also Keycloak will prefer it : https://github.com/keycloak/keycloak/discussions/29205

In the conf/cache-ispn.xml default configuration, we replace the stack :

<jgroups>
    <stack name="jdbc-ping-tcp" extends="tcp">
      <JDBC_PING connection_driver="org.postgresql.Driver"
                 connection_username="keycloak" connection_password="password"
                 connection_url="jdbc:postgresql://${env.KC_DB_USERNAME}:${env.KC_DB_PASSWORD}@${env.KC_DB_URL_HOST}:${env.KC_DB_URL_PORT}/${env.KC_DB_URL_DATABASE}"
                 initialize_sql="CREATE TABLE IF NOT EXISTS JGROUPSPING (own_addr varchar(200) NOT NULL, cluster_name varchar(200) NOT NULL, ping_data BYTEA, constraint PK_JGROUPSPING PRIMARY KEY (own_addr, cluster_name));"
                 info_writer_sleep_time="500"
                 remove_all_data_on_view_change="true"
                 stack.combine="REPLACE"
                 stack.position="MPING" />
    </stack>
</jgroups>

Add this file to conf directory in Keycloak :

build.sh:

cp cache-ispn.xml keycloak/conf/cache-ispn.xml

And in .env :

export KC_CACHE_CONFIG_FILE=cache-ispn.xml

And now, scaling is possible :

https://dokku.com/docs/processes/process-management/

SPIs

Previously, we created an empty pom.xml file. This file is executed with Maven on build, we shall use it to build custom modules (“SPIs”) in Keycloak.

We will deploy an authenticator we have the code on github : https://github.com/please-openit/keycloak-authenticator-filter-ip

Add the code in a “spis” directory.

Use “modules” :

    <modules>
        <module>spis/keycloak-authenticator-filter-ip</module>
    </modules>

And a build that copies generated files into a “target” directory at root :

<build>
    <plugins>
        <plugin>
            <artifactId>maven-resources-plugin</artifactId>
            <version>3.0.2</version>
            <executions>
                <execution>
                    <id>copy-resource-one</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-resources</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${basedir}/target</outputDirectory>
                        <resources>
                            <resource>
                                <directory>spis/keycloak-authenticator-filter-ip/deployments/</directory>
                                <includes>
                                    <include>please-open.it-authenticator-filter-ip-1.0-SNAPSHOT-jar-with-dependencies.jar</include>
                                </includes>
                            </resource>
                        </resources>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

After that, in the build phase get the generated jars and put them into “providers” directory :

cp -R target/* keycloak/providers/

Boom ! SPIs are built and deployed :

mathieupassenaud@device-106 keycloak-test % git push --force                                                      
Enumerating objects: 64, done.
Counting objects: 100% (64/64), done.
Delta compression using up to 8 threads
Compressing objects: 100% (44/44), done.
Writing objects: 100% (58/58), 2.14 MiB | 12.42 MiB/s, done.
Total 58 (delta 10), reused 0 (delta 0), pack-reused 0
-----> Cleaning up...
-----> Building keycloak from herokuish
-----> Adding BUILD_ENV to build environment...
       BUILD_ENV added successfully
-----> Fetching custom buildpack
-----> Java app detected
-----> Installing OpenJDK 21... done
-----> Installing Maven 3.9.4... done
-----> Executing Maven
       $ mvn -DskipTests clean dependency:list install
       [INFO] Scanning for projects...
       [INFO] ------------------------------------------------------------------------
       [INFO] Reactor Build Order:
       [INFO]
       [INFO] authenticator-filter-ip                                            [jar]
       [INFO] keycloak                                                           [pom]
       [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-assembly-plugin/3.3.0/maven-assembly-plugin-3.3.0.pom
       [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-assembly-plugin/3.3.0/maven-assembly-plugin-3.3.0.pom (16 kB at 29 kB/s)
       [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-assembly-plugin/3.3.0/maven-assembly-plugin-3.3.0.jar
       [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-assembly-plugin/3.3.0/maven-assembly-plugin-3.3.0.jar (242 kB at 2.1 MB/s)
       [INFO]
       [INFO] ---------------< please-open.it:authenticator-filter-ip >---------------
       [INFO] Building authenticator-filter-ip 1.0-SNAPSHOT                      [1/2]
       [INFO]   from spis/keycloak-authenticator-filter-ip/pom.xml
       [INFO] --------------------------------[ jar ]---------------------------------
        ...
       [INFO]
       [INFO] authenticator-filter-ip ............................ SUCCESS [ 40.432 s]
       [INFO] keycloak ........................................... SUCCESS [  0.266 s]
       [INFO] ------------------------------------------------------------------------
       [INFO] BUILD SUCCESS
       [INFO] ------------------------------------------------------------------------
       [INFO] Total time:  42.295 s
       [INFO] Finished at: 2024-12-03T13:29:22Z
       [INFO] ------------------------------------------------------------------------
       ...
       Keycloak 26.0.6 downloaded and unzipped successfuly
       JAVA_OPTS already set in environment; overriding default settings
       Updating the configuration and installing your custom providers, if any. Please wait.
       2024-12-03 13:34:31,663 WARN  [org.key.qua.run.cli.Picocli] (main) The following run time options were found, but will be ignored during build time: kc.cache-config-file, kc.db-url-host, kc.db-url-database, kc.db-url-port, kc.db-username, kc.db-password, kc.hostname, kc.http-enabled, kc.http-port, kc.proxy-headers
       2024-12-03 13:34:39,343 WARN  [org.key.services] (build-50) KC-SERVICES0047: filterIP (it.pleaseopen.authenticator.filterip.FilterIPAuthenticatorFactory) is implementing the internal SPI authenticator. This SPI is internal and may change without notice
       2024-12-03 13:34:39,518 WARN  [org.key.services] (build-50) KC-SERVICES0047: filterIPClient (it.pleaseopen.authenticator.filterip.FilterIPAuthenticatorClientFactory) is implementing the internal SPI client-authenticator. This SPI is internal and may change without notice
       2024-12-03 13:34:52,027 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 18497ms
       Server configuration updated and persisted. Run the following command to review the configuration:
        kc.sh show-config

The authenticator is ready to use :

alt text

Conclusion

From nothing to a fully set up Keycloak with custom SPIs in a few minutes (ok… hours) on a platform you should try for all your custom applications : push the source code to production.

If you need a more “up and ready” dedicated Keycloak, we work with clever-cloud.com on their keycloak as a service.