-
-
Save sarath-soman/5d9aec06953bbd0990c648605d4dba07 to your computer and use it in GitHub Desktop.
# Keycloak containers doesn't come with curl or wget in it, this forces the users to use alternative mechanisms to realise | |
# health check for the keycloak standard containers. This example leverages the capability of modern Java to dynamically | |
# compile a *.java source file and execute it on the fly using the `java` command. The HealthCheck class uses | |
# java.net.URL to open a connection to the `health/live` endpoint of keycloak and exits the process with a non-zero status | |
# if the http status is not `Ok` | |
version: '3' | |
services: | |
############################ | |
# Keycloak service | |
############################ | |
keycloak: | |
image: quay.io/keycloak/keycloak:22.0.5 | |
command: | |
- start-dev | |
- --import-realm | |
environment: | |
KEYCLOAK_ADMIN: admin | |
KEYCLOAK_ADMIN_PASSWORD: admin | |
DB_VENDOR: h2 | |
KC_HEALTH_ENABLED: true | |
ports: | |
- '8080:8080' | |
volumes: | |
- ./keycloak:/opt/keycloak/data/import | |
healthcheck: | |
test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java /tmp/HealthCheck.java http://localhost:8080/health/live'] | |
interval: 5s | |
timeout: 5s | |
retries: 30 | |
can also do
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080;echo -e \"GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n\" >&3;grep \"HTTP/1.1 200 OK\" <&3"]
interval: 5s
timeout: 5s
retries: 3
can also do
healthcheck: test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080;echo -e \"GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n\" >&3;grep \"HTTP/1.1 200 OK\" <&3"] interval: 5s timeout: 5s retries: 3
👍
Thanks a lot!
Happy to help
Thanks for sharing! 💯
Thanks for sharing! 💯
👍
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/8080;echo -e 'GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n' >&3;if [ $? -eq 0 ]; then echo 'Healthcheck Successful';exit 0;else echo 'Healthcheck Failed';exit 1;fi;"]
@patrickmichalina in my case keycloak:latest
this failed still by exit 1 other than 0, so I modified it a bit.
@michael-riha as of keycloak v25 the healthchecks metric endpoint is served on a different port which is by default port 9000
I am doing this:
- in the docker-compose:
... volumes: - /path/to/healthcheck.sh:/opt/keycloak/bin/healthcheck.sh healthcheck: test: /opt/keycloak/bin/healthcheck.sh || exit 1 ...
- the healthcheck.sh is
/opt/keycloak/bin/kcadm.sh config credentials --server http://localhost:8080/${KC_HTTP_RELATIVE_PATH}/ --realm master --user ${KEYCLOAK_ADMIN} --password ${KEYCLOAK_ADMIN_PASSWORD} /opt/keycloak/bin/kcadm.sh get http://localhost:9000/${KC_HTTP_RELATIVE_PATH}/health
working for me using keycloak/keycloak:25.0.1
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000;echo -e 'GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n' >&3;if [ $? -eq 0 ]; then echo 'Healthcheck Successful';exit 0;else echo 'Healthcheck Failed';exit 1;fi;"]
interval: 30s
timeout: 10s
retries: 3
@marco-carvalho indeed this is the only solution that worked for me on the latest version.
@marco-carvalho thanks a lot!
Your solution works also with Keycloak 25.0.6.
@marco-carvalho great, working for Keycloak 26.0.0
@marco-carvalho I get the following error in the container logs if I use the health check with Keycloak 26.0.0:
ERROR [io.vertx.ext.web.RoutingContext] (vert.x-eventloop-thread-11) Unhandled exception in router
@Angi2412 there is the kcadm.sh
script that, once authenticated, can do get
requests to the health
endpoints: see here.
Just adapt it to your needs (e.g remove KC_HTTP_RELATIVE_PATH
if you don't have it), and remember to give execution permissions to healthcheck.sh
script before to mount it into the container!
In addition, it would be better to check response for UP
. Let's add it:
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000; echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost:9000\r\nConnection: close\r\n\r\n' >&3;cat <&3 | grep -q '\"status\": \"UP\"' && exit 0 || exit 1"]
interval: 30s
timeout: 10s
retries: 3
@maxixcom Thank you! This is working great.
@marco-carvalho it's working localy, but not working in Github actions the container remains in waiting state.... any idea ?
@marco-carvalho it's working localy, but not working in Github actions the container remains in waiting state.... any idea ?
Is the KC_HEALTH_ENABLED
set to true
when you are running the action? This healthcheck shown by @marco-carvalho only works if the endpoint health/ready
is enabled, and it's this env var responsible for that.
sry, on my side it's working now... the good version was in github actions because my docker image cache was not clean.
This one working good locally and in githubaction with keycloak 26.0
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000;echo -e 'GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n' >&3;if [ $? -eq 0 ]; then echo 'Healthcheck Successful';exit 0;else echo 'Healthcheck Failed';exit 1;fi;"]
start_period: 10s
interval: 30s
retries: 3
timeout: 5s
Here’s my take, including a bonus step for setting up an external database and schema creation. This is important because Keycloak (as of version 26.0.7) fails to start if the schema doesn’t already exist.
Important
Run dos2unix on both files (dos2unix .env docker-compose.yml
) before spinning up the containers (docker compose up
) on Windows, as Windows line breaks can break some file parsers.
Important
Make sure you’re using the latest versions of Docker (docker --version
: 27.4.1-rd, build 1707ac7) and Docker Compose (docker compose version
: v2.29.5). Also, check that your system has no pending updates, since ongoing driver updates in particular might interfere with Docker.
Note
To ensure the health check isn’t returning a false positive, comment out the line KC_HTTP_MANAGEMENT_PORT: ${ENV_KC_HTTP_MANAGEMENT_PORT:?}
in the keycloak
service to confirm that it fails. Before doing so, you can prune everything with docker system prune
to ensure it wasn’t just a coincidence.
# .env
# POSTGRES
ENV_PG_CONTAINER_IMAGE_NAME=postgres
ENV_PG_CONTAINER_IMAGE_VERSION=17.2-bookworm
ENV_PG_NAME=dev_db
ENV_PG_USER=admin
ENV_PG_PASSWORD=admin
ENV_PG_PORT=5432
# KEYCLOAK
ENV_KC_CONTAINER_IMAGE_NAME=keycloak/keycloak
ENV_KC_CONTAINER_IMAGE_VERSION=26.1.0-0
ENV_KC_SCHEMA=keycloak
ENV_KC_BOOTSTRAP_ADMIN_USERNAME=${ENV_PG_USER}
ENV_KC_BOOTSTRAP_ADMIN_PASSWORD=${ENV_PG_PASSWORD}
ENV_KC_HTTP_PORT=8081
ENV_KC_HTTP_MANAGEMENT_PORT=9081
ENV_KC_HOSTNAME_STRICT=false
ENV_KC_HEALTH_ENABLED=true
# KEYCLOAK ADMIN REST BOOTSTRAP CLIENT
ENV_KC_ADMIN_REST_BOOTSTRAP_CLIENT_ID=bootstrap-client
ENV_KC_ADMIN_REST_BOOTSTRAP_CLIENT_SECRET=${ENV_KC_ADMIN_REST_BOOTSTRAP_CLIENT_ID}
# docker-compose.yml
services:
postgres:
image: ${ENV_PG_CONTAINER_IMAGE_NAME:?}:${ENV_PG_CONTAINER_IMAGE_VERSION:?}
environment:
POSTGRES_DB: ${ENV_PG_NAME:?}
POSTGRES_USER: ${ENV_PG_USER:?}
POSTGRES_PASSWORD: ${ENV_PG_PASSWORD:?}
ports:
- "${ENV_PG_PORT:?}:5432"
healthcheck:
test: [ "CMD", "pg_isready", "-U", "${ENV_PG_USER:?}", "-d", "${ENV_PG_NAME:?}" ]
start_period: 5s
interval: 5s
timeout: 3s
retries: 5
script_create_kc_schema:
image: ${ENV_PG_CONTAINER_IMAGE_NAME:?}:${ENV_PG_CONTAINER_IMAGE_VERSION:?}
depends_on:
postgres:
condition: service_healthy
restart: "no"
entrypoint: >
bash -c
"
PGPASSWORD='${ENV_PG_PASSWORD:?}' psql -h postgres -U '${ENV_PG_USER}' -d '${ENV_PG_NAME:?}' -tc \"
SELECT 1
FROM pg_namespace
WHERE nspname = '${ENV_KC_SCHEMA:?}';
\" | grep -q 1;
if [ $? -eq 0 ]; then
echo '[INFO] Schema \"${ENV_KC_SCHEMA:?}\" already exists.';
else
PGPASSWORD='${ENV_PG_PASSWORD:?}' psql -h postgres -U '${ENV_PG_USER:?}' -d '${ENV_PG_NAME:?}' -c \"
CREATE SCHEMA ${ENV_KC_SCHEMA:?};
\";
if [ $? -ne 0 ]; then
echo '[ERROR] Failed to create schema \"${ENV_KC_SCHEMA:?}\".';
exit 1;
fi;
echo '[INFO] Schema \"${ENV_KC_SCHEMA:?}\" created successfully.';
fi;
exit 0;
"
keycloak:
image: ${ENV_KC_CONTAINER_IMAGE_NAME}:${ENV_KC_CONTAINER_IMAGE_VERSION}
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/${ENV_PG_NAME}
KC_DB_SCHEMA: ${ENV_KC_SCHEMA:?}
KC_DB_USERNAME: ${ENV_PG_USER:?}
KC_DB_PASSWORD: ${ENV_PG_PASSWORD:?}
KC_BOOTSTRAP_ADMIN_USERNAME: ${ENV_KC_BOOTSTRAP_ADMIN_USERNAME:?}
KC_BOOTSTRAP_ADMIN_PASSWORD: ${ENV_KC_BOOTSTRAP_ADMIN_PASSWORD:?}
KC_BOOTSTRAP_ADMIN_CLIENT_ID: ${ENV_KC_ADMIN_REST_BOOTSTRAP_CLIENT_ID:?}
KC_BOOTSTRAP_ADMIN_CLIENT_SECRET: ${ENV_KC_ADMIN_REST_BOOTSTRAP_CLIENT_SECRET:?}
KC_HTTP_PORT: ${ENV_KC_HTTP_PORT:?}
KC_HOSTNAME_STRICT: ${ENV_KC_HOSTNAME_STRICT:?}
KC_HEALTH_ENABLED: ${ENV_KC_HEALTH_ENABLED:?}
KC_HTTP_MANAGEMENT_PORT: ${ENV_KC_HTTP_MANAGEMENT_PORT:?}
ports:
- "${ENV_KC_HTTP_PORT}:${ENV_KC_HTTP_PORT}"
- "${ENV_KC_HTTP_MANAGEMENT_PORT}:${ENV_KC_HTTP_MANAGEMENT_PORT}"
depends_on:
postgres:
condition: service_healthy
script_create_kc_schema:
condition: service_completed_successfully
healthcheck:
test: [
"CMD-SHELL",
"exec 3<>/dev/tcp/localhost/${ENV_KC_HTTP_MANAGEMENT_PORT:?}; \
echo -en 'GET /health/ready' >&3; \
# Give the server a moment to respond, then search for 'UP'
if timeout 3 cat <&3 | grep -m 1 'UP'; then \
exec 3<&-; exec 3>&-; exit 0; \
else \
exec 3<&-; exec 3>&-; exit 1; \
fi"
]
start_period: 10s
interval: 5s
timeout: 2s
retries: 20
command: start-dev
script_check_kc_healthy:
image: ${ENV_KC_CONTAINER_IMAGE_NAME}:${ENV_KC_CONTAINER_IMAGE_VERSION}
depends_on:
keycloak:
condition: service_healthy
entrypoint: >
bash -c
"echo '[INFO] Keycloak is healthy. Exiting now.'; exit 0;"
Verify the "Installing additional RPM packages" subject at https://www.keycloak.org/server/containers
but, always exists others ways....
keycloak-service:
container_name: keycloak-container
image: quay.io/keycloak/keycloak:26.0.7
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_HOSTNAME: localhost
KC_HOSTNAME_PORT: 8080
KC_HOSTNAME_STRICT_BACKCHANNEL: 'true'
KC_HOSTNAME_STRICT_FRONTCHANNEL: 'true'
KC_HTTP_ENABLED: 'true' #PRD false
KC_HEALTH_ENABLED: 'true'
KC_METRICS_ENABLED: 'true'
KC_HTTP_METRICS_HISTOGRAMS_ENABLED: 'true'
KC_CACHE_METRICS_HISTOGRAMS_ENABLED: 'true'
KC_LOG_LEVEL: INFO # DEBUG
# DB Configuration
KC_DB: postgres
KC_DB_VENDOR: postgres
KC_DB_SCHEMA: public
KC_DB_HOST: postgresHost
KC_DB_PORT: 5432
KC_DB_NAME: keycloakDB
KC_DB_URL: jdbc:postgresql://postgres-container:5432/keycloakDB
KC_DB_USERNAME: userKeyCloak
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
depends_on:
postgres-service:
condition: service_healthy
healthcheck:
test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java /tmp/HealthCheck.java http://localhost:9000/health/live']
interval: 5s
timeout: 5s
retries: 30
command: ['start-dev', '--http-port', '8080']
ports:
- 8087:8080
- 7447:7443
- 9007:9000
networks:
- local_network
labels:
- maintainer="Felipe Augusto, Canal DEPLOY, https://linktr.ee/felipementel"
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
cpuset: '1'
sry, on my side it's working now... the good version was in github actions because my docker image cache was not clean.
This one working good locally and in githubaction with keycloak 26.0
healthcheck: test: ["CMD-SHELL", "exec 3<>/dev/tcp/127.0.0.1/9000;echo -e 'GET /health/ready HTTP/1.1\r\nhost: http://localhost\r\nConnection: close\r\n\r\n' >&3;if [ $? -eq 0 ]; then echo 'Healthcheck Successful';exit 0;else echo 'Healthcheck Failed';exit 1;fi;"] start_period: 10s interval: 30s retries: 3 timeout: 5s
Starting with 26.0.8 for me this leads to the following log entries:
keycloak-1 | 2025-01-16T11:42:36.127925007Z 2025-01-16 11:42:36,127 ERROR [io.vertx.ext.web.RoutingContext] (vert.x-eventloop-thread-3) Unhandled exception in router
healthcheck:
test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java /tmp/HealthCheck.java http://localhost:9000/health/live']
interval: 5s
timeout: 5s
retries: 30
try to use your healthcheck like this
healthcheck:
test: ['CMD-SHELL', '[ -f /tmp/HealthCheck.java ] || echo "public class HealthCheck { public static void main(String[] args) throws java.lang.Throwable { System.exit(java.net.HttpURLConnection.HTTP_OK == ((java.net.HttpURLConnection)new java.net.URL(args[0]).openConnection()).getResponseCode() ? 0 : 1); } }" > /tmp/HealthCheck.java && java /tmp/HealthCheck.java http://localhost:9000/health/live']
interval: 5s
timeout: 5s
retries: 30
For what it’s worth, I’ve updated my previous answer [1] to bump the Keycloak version to v26.1.0-0 (released on 2025-01-15), and I confirm that no changes to the health check logic were necessary.
Thanks a lot!