This quickstart is for educational purposes only and should not be used in production. It demonstrates how to configure HAProxy as a TLS re-encrypt load balancer in front of a clustered Keycloak deployment.
In TLS re-encrypt mode, the load balancer decrypts the incoming HTTPS connection and establishes a new HTTPS connection to the backend service. HAProxy operates at the HTTP layer (Layer 7) and has a direct access to the HTTP content.
- HAProxy can inspect, modify, and cache HTTP headers and the request body. The end-to-end encryption between the client and Keycloak is not preserved.
- HAProxy holds a TLS certificate and a private key used to authenticate itself to the client.
- HAProxy holds a TLS certificate and a private key used to authenticate itself to Keycloak.
- Keycloak holds a TLS certificate and a private key used to authenticate itself to HAProxy.
- HAProxy listens on port 8443, terminates the incoming HTTPS connections and reencrypts the requests before forwarding them to Keycloak instances.
It uses the
ForwardedHTTP header to pass the original client IP address to Keycloak. It is the only container attached to thefrontendnetwork, making it the single entry point. - Keycloak 1 & 2 are clustered via embedded Infinispan and share the same PostgreSQL database.
They live exclusively on the
backendnetwork, which is marked asinternaland unreachable from the host. - PostgreSQL provides the shared database for Keycloak on the
backendnetwork.
- Docker and Docker Compose
openssl(for certificate generation)
./generate-certs.sh <hostname>This example uses nip.io, a DNS service that maps 127.0.0.1.nip.io to 127.0.0.1, avoiding the need to edit /etc/hosts:
./generate-certs.sh 127.0.0.1.nip.ioKC_HOST=<hostname> docker compose up -dFor example:
KC_HOST=127.0.0.1.nip.io docker compose up -dOnce the services are up, Keycloak is available at https://<hostname>:8443.
Log in to the admin console using credentials admin / admin.
The browser will show a certificate warning because the certificate is self-signed. This is expected and can be safely accepted for local testing.
Open http://127.0.0.1.nip.io:8404/stats in a browser to verify that both Keycloak backends are healthy.
This is a walkthrough through a graceful shutdown of one of the Keycloak instances:
- Open http://127.0.0.1.nip.io:8404/stats in a browser to verify that both Keycloak backends are healthy.
- Send a
TERMsignal to one of the Keycloak containers for a graceful shutdown (takes 30 seconds). Container exits with code 143.docker compose stop keycloak1 -t 60
- Observe that after 3x5=15 seconds the
keycloak1backend turns UP/green to UP/yellow and eventually to DOWN/red. Requests are still served by the node until it shuts down gracefully after 30 seconds. - Start the Keycloak container again:
docker compose start keycloak1
- Observe that after 2x5=10 seconds the
keycloak1backend turns DOWN/yellow and eventually UP/green.
docker compose downThe key parts of haproxy.cfg are explained below.
Certificate for external access:
bind *:8443 ssl crt /mnt/certs/haproxy-external.pem
HAProxy will use this certificate to authenticate itself to the client.
HTTP mode for TLS re-encrypt:
mode http
HAProxy operates in HTTP mode (Layer 7), decrypting and reencrypting HTTP traffic before forwarding to Keycloak.
Replace the Forwarded and X-Forwarded- headers:
http-request del-header Forwarded
http-request del-header x-forwarded-for
http-request del-header x-forwarded-proto
http-request del-header x-forwarded-host
http-request del-header x-forwarded-port
http-request del-header x-forwarded-server
This configuration will drop Forwarded and X-Forwarded- HTTP headers on the proxy, preventing the client from providing misleading information to the backend server.
Note: If the KC_PROXY_HEADERS setting is set to forwarded (see below) Keycloak will only accept the standard Forwarded header and ignore any X-Forwarded- headers. In this case it is not strictly necessary to filter them on the proxy.
option forwarded host by by_port for
The above configuration will make HAProxy add a standard Forwarded HTTP header with the actual information from the incoming connection.
HTTP health check on the management port:
option httpchk GET /health/ready
http-check expect status 200
HAProxy performs health checks against Keycloak's management endpoint /health/ready, expecting an HTTP 200 response.
This endpoint is only available when Keycloak is configured with KC_HEALTH_ENABLED=true and KC_METRICS_ENABLED=true.
Server lines:
server keycloak1 keycloak1:8443 ssl verify required crt /mnt/certs/haproxy-internal.pem ca-file /mnt/certs/keycloak1-cert.pem check port 9000 check-ssl verify none inter 5s fall 3 rise 2
-
ssl verify requiredenables a secured connection to Keycloak. -
crt /mnt/certs/haproxy-internal.pemconfigures the certificate used to authenticate HAProxy to Keycloak. -
ca-file /mnt/certs/keycloak1-cert.pemconfigures the certificate used to authenticate Keycloak to HAProxy. -
check port 9000 check-ssl verify nonedirects health checks to the management port (9000) over HTTPS, skipping certificate verification for the health check connection. -
inter 5s fall 3 rise 2configures the health check frequency: poll every 5 seconds, mark a server as down after 3 consecutive failures, and mark it as up again after 2 consecutive successes.
Graceful shutdown timing:
With the values above, it may take up to 15 seconds (3 failures x 5s interval) for HAProxy to detect that a Keycloak instance is down.
For this reason, Keycloak is configured with KC_SHUTDOWN_DELAY=30s and
KC_SHUTDOWN_TIMEOUT=30s, giving HAProxy enough time to detect the shutdown and allowing existing client connections to drain gracefully.
Configure accepted proxy headers:
KC_PROXY_HEADERS: forwarded
Keycloak will only accept the standard Forwarded HTTP header, ignoring any X-Forwarded- headers.
Configure the certificate and private key for HTTPS:
KC_HTTPS_CERTIFICATE_FILE: /opt/keycloak/certs/cert.pem
KC_HTTPS_CERTIFICATE_KEY_FILE: /opt/keycloak/certs/key.pem
The certificate is used by HAProxy to authenticate the Keycloak server.
Configure mTLS:
KC_HTTPS_CLIENT_AUTH: required
KC_HTTPS_TRUST_STORE_FILE: /opt/keycloak/conf/https-truststore/haproxy-internal-cert.pem
Keycloak will require client authentication via certificates provided in the truststore. In this case the only provided certificate is for authenticating HAProxy on the internal network.
KC_HTTPS_MANAGEMENT_CLIENT_AUTH: none
This setting disables the client authentication requirement for the management endpoint.