From e1a0edd14c786943e651a5e565f2b777b6299a8a Mon Sep 17 00:00:00 2001 From: Ruchika Date: Wed, 28 Jan 2026 13:42:58 +0000 Subject: [PATCH 1/9] Enable graceful HTTP shutdown and document default behavior Closes #43589 Signed-off-by: Ruchika --- docs/guides/server/reverseproxy.adoc | 32 +++++++++++++++++++ .../src/main/resources/application.properties | 7 ++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/docs/guides/server/reverseproxy.adoc b/docs/guides/server/reverseproxy.adoc index 24cc0fa87a55..ab09aaa8ebc1 100644 --- a/docs/guides/server/reverseproxy.adoc +++ b/docs/guides/server/reverseproxy.adoc @@ -278,3 +278,35 @@ The default values of the options are as follows: If your certificate chain is longer than the given default, you must define the option with an appropriate number. Otherwise, the provider will discard the request. + +== Graceful HTTP shutdown + +When running {project_name} behind a reverse proxy or load balancer , it is important to allow in-flight requests to complete during server shutdown. + +{project_name} enables graceful HTTP shutdown by default using Quarkus runtime configuration. + +=== Default behavior + +By default {project_name} configures Quarkus with short pre-shutdown delay and a bounded shutdown timeout: + +[source,properties] +---- +quarkus.shutdown.delay-enabled=true +quarkus.shutdown.delay=1s +quarkus.shutdown.timeout=1s +---- + +When shutdown is initiated: + +* A pre-shutdown is applied, allowing load balancers to detect that the server is terminating and stop routing new requests to it. +* Existing HTTP requests continue to be processed. +* The server waits up to the configured shutdown timeout for in-flight requests to complete before exiting. + +=== Overriding shutdown configuration + +Advanced users may override the default graceful shutdown behavior using standard Quarkus configuration mechanisms. + +These values can be set in application.properties file. + +NOTE: When Quarkus properties are defined directly in application.properties, environment variables may not override them. +This is general Quarkus configuration behavior. diff --git a/quarkus/runtime/src/main/resources/application.properties b/quarkus/runtime/src/main/resources/application.properties index d6bb3efb8208..2fc4ddafd597 100644 --- a/quarkus/runtime/src/main/resources/application.properties +++ b/quarkus/runtime/src/main/resources/application.properties @@ -106,6 +106,9 @@ mp.openapi.extensions.smallrye.duplicateOperationIdBehavior=FAIL # Disable Error messages from smallrye.openapi # related issue: https://github.com/keycloak/keycloak/issues/41871 quarkus.log.category."io.smallrye.openapi.runtime.scanner.dataobject".level=off - # 3.31.1 appears to have a hibernate bug -quarkus.log.category."org.hibernate.orm.sql.exec".level=info \ No newline at end of file +quarkus.log.category."org.hibernate.orm.sql.exec".level=info +quarkus.shutdown.delay=1s +# Graceful shutdown defaults +quarkus.shutdown.delay-enabled=true +quarkus.shutdown.timeout=1s \ No newline at end of file From 4c779c9f10fbaf07c369b84a38b26e430e766e6b Mon Sep 17 00:00:00 2001 From: Ruchika Date: Wed, 11 Feb 2026 16:57:29 +0000 Subject: [PATCH 2/9] Fixed the quarkus properties using CLI and updated the document as per the PR review comments Closes #43589 Signed-off-by: Ruchika --- docs/guides/server/reverseproxy.adoc | 118 +++++++++++++++--- .../java/org/keycloak/config/HttpOptions.java | 12 ++ .../mappers/HttpPropertyMappers.java | 24 ++++ .../src/main/resources/application.properties | 3 +- ...mandDistTest.testStartDevHelp.approved.txt | 10 ++ ...dDistTest.testStartDevHelpAll.approved.txt | 10 ++ ...CommandDistTest.testStartHelp.approved.txt | 10 ++ ...mandDistTest.testStartHelpAll.approved.txt | 10 ++ ...stTest.testStartOptimizedHelp.approved.txt | 10 ++ ...est.testStartOptimizedHelpAll.approved.txt | 10 ++ ...tUpdateCompatibilityCheckHelp.approved.txt | 10 ++ ...dateCompatibilityCheckHelpAll.approved.txt | 10 ++ ...dateCompatibilityMetadataHelp.approved.txt | 10 ++ ...eCompatibilityMetadataHelpAll.approved.txt | 10 ++ 14 files changed, 239 insertions(+), 18 deletions(-) diff --git a/docs/guides/server/reverseproxy.adoc b/docs/guides/server/reverseproxy.adoc index ab09aaa8ebc1..9895f9c3dc5d 100644 --- a/docs/guides/server/reverseproxy.adoc +++ b/docs/guides/server/reverseproxy.adoc @@ -281,32 +281,118 @@ Otherwise, the provider will discard the request. == Graceful HTTP shutdown -When running {project_name} behind a reverse proxy or load balancer , it is important to allow in-flight requests to complete during server shutdown. +When running {project_name} behind a reverse proxy or load balancer, graceful shutdown ensures that in-flight requests complete successfully during server termination, preventing connection errors for clients. -{project_name} enables graceful HTTP shutdown by default using Quarkus runtime configuration. +{project_name} enables graceful HTTP shutdown by default with configurable timeouts. + +=== Understanding shutdown phases + +The shutdown process consists of two phases: + +*Pre-shutdown delay*:: During this phase, {project_name} signals to load balancers and proxies that it is preparing to shut down. +The server's readiness endpoint returns a "not ready" status, allowing the load balancer to stop routing new requests to this instance. +Existing TLS and HTTP keepalive connections are allowed to drain naturally. +The server continues to process existing requests. + +*Shutdown timeout*:: After the pre-shutdown delay, {project_name} stops accepting new requests and waits for in-flight HTTP requests to complete. +If requests are still running after the timeout expires, the server shuts down regardless. === Default behavior -By default {project_name} configures Quarkus with short pre-shutdown delay and a bounded shutdown timeout: +By default, {project_name} is configured with a 1-second pre-shutdown delay and a 1-second shutdown timeout. +These defaults work well for most standard deployments where: + +* The load balancer reconfigures quickly (within 1 second) +* Most requests complete within 1 second +* The reverse proxy uses edge termination or re-encryption (not TLS passthrough) + +=== Configuring shutdown timeouts + +Advanced users can adjust the shutdown timeouts using CLI options based on their deployment characteristics. -[source,properties] +[source,bash] ---- -quarkus.shutdown.delay-enabled=true -quarkus.shutdown.delay=1s -quarkus.shutdown.timeout=1s +bin/kc.sh start --shutdown-delay= --shutdown-timeout= ---- -When shutdown is initiated: +Available options: -* A pre-shutdown is applied, allowing load balancers to detect that the server is terminating and stop routing new requests to it. -* Existing HTTP requests continue to be processed. -* The server waits up to the configured shutdown timeout for in-flight requests to complete before exiting. +`--shutdown-delay`:: +Length of the pre-shutdown phase during which the server prepares for shutdown. +This period allows for load balancer reconfiguration and draining of TLS/HTTP keepalive connections. +Default: `1s` -=== Overriding shutdown configuration +`--shutdown-timeout`:: +The shutdown period waiting for currently running HTTP requests to finish. +Default: `1s` + +Both values accept duration formats: `1s` (seconds), `500ms` (milliseconds), `2m` (minutes), etc. + +=== When to adjust shutdown timeouts + +Consider adjusting these values based on your deployment configuration: + +[%header,cols="2,1,1,3"] +|=== +|Scenario |Delay |Timeout |Reason + +|Load balancer polls readiness probe +|10s +|1-10s +|Allow 1-2 poll cycles (typically 5s intervals) for load balancer to detect shutdown + +|TLS passthrough configuration +|1s +|10-30s +|Longer timeout allows keepalive connections to drain naturally and receive connection close signals + +|Long-running admin API requests +|1s +|10-30s +|Admin operations may take longer than typical user requests + +|Test environments / quick restarts +|0s +|500ms +|Minimize shutdown time when graceful draining is not needed + +|Deployment orchestration notifies proxy before pod termination +|0s +|1-10s +|No pre-shutdown delay needed if proxy is already reconfigured + +|Combined: TLS passthrough + polled readiness +|10s +|30s +|Delays add up: time for load balancer detection + connection draining + +|=== -Advanced users may override the default graceful shutdown behavior using standard Quarkus configuration mechanisms. +==== Example configurations -These values can be set in application.properties file. +For production with TLS passthrough: -NOTE: When Quarkus properties are defined directly in application.properties, environment variables may not override them. -This is general Quarkus configuration behavior. +[source,bash] +---- +bin/kc.sh start --shutdown-delay=5s --shutdown-timeout=30s +---- + +For load balancers that poll readiness: + +[source,bash] +---- +bin/kc.sh start --shutdown-delay=10s --shutdown-timeout=10s +---- + +For test environments: + +[source,bash] +---- +bin/kc.sh start-dev --shutdown-delay=0s --shutdown-timeout=500ms +---- + +[NOTE] +==== +The shutdown delay affects the minimum time required for a complete server restart. +In Kubernetes environments, ensure your `terminationGracePeriodSeconds` is longer than the sum of `shutdown-delay` and `shutdown-timeout` to prevent forced termination. +==== diff --git a/quarkus/config-api/src/main/java/org/keycloak/config/HttpOptions.java b/quarkus/config-api/src/main/java/org/keycloak/config/HttpOptions.java index acbe61c24726..f4406684b193 100644 --- a/quarkus/config-api/src/main/java/org/keycloak/config/HttpOptions.java +++ b/quarkus/config-api/src/main/java/org/keycloak/config/HttpOptions.java @@ -150,4 +150,16 @@ public enum ClientAuth { .defaultValue(Boolean.FALSE) .build(); + public static final Option SHUTDOWN_TIMEOUT = new OptionBuilder<>("shutdown-timeout", String.class) + .category(OptionCategory.HTTP) + .description("The shutdown period waiting for currently running HTTP requests to finish. " + DURATION_DESCRIPTION) + .defaultValue("1s") + .build(); + + public static final Option SHUTDOWN_DELAY = new OptionBuilder<>("shutdown-delay", String.class) + .category(OptionCategory.HTTP) + .description("Length of the pre-shutdown phase during which the server prepares for shutdown. " + DURATION_DESCRIPTION + + " This period allows for loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections.") + .defaultValue("1s") + .build(); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java index c74defdfac38..36da54a45abf 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import java.time.Duration; import java.util.List; import java.util.Optional; @@ -19,6 +20,7 @@ import org.keycloak.quarkus.runtime.cli.command.AbstractCommand; import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.configuration.DurationConverter; import io.quarkus.runtime.util.ClassPathUtils; import io.quarkus.vertx.http.runtime.options.TlsUtils; import io.smallrye.config.ConfigSourceInterceptorContext; @@ -182,8 +184,19 @@ public List> getPropertyMappers() { .to("quarkus.rest.jackson.optimization.enable-reflection-free-serializers") .build(), fromOption(HttpOptions.HTTP_ACCEPT_NON_NORMALIZED_PATHS) + .build(), + fromOption(HttpOptions.SHUTDOWN_TIMEOUT) + .to("quarkus.shutdown.timeout") + .paramLabel("timeout") + .validator(HttpPropertyMappers::validateDuration) + .build(), + fromOption(HttpOptions.SHUTDOWN_DELAY) + .to("quarkus.shutdown.delay") + .paramLabel("delay") + .validator(HttpPropertyMappers::validateDuration) .build() ); + } @Override @@ -242,4 +255,15 @@ private static String resolveMaxThreads(String value, } return value; } + + private static void validateDuration(String value) { + try { + Duration duration = DurationConverter.parseDuration(value); + if (duration.compareTo(Duration.ofSeconds(1)) < 0) { + throw new PropertyException("Duration must be at least 1 second."); + } + } catch (IllegalArgumentException e) { + throw new PropertyException("Invalid duration format. Expected format examples: 1s, 30s, 1m, etc."); + } + } } diff --git a/quarkus/runtime/src/main/resources/application.properties b/quarkus/runtime/src/main/resources/application.properties index 2fc4ddafd597..b632403411d4 100644 --- a/quarkus/runtime/src/main/resources/application.properties +++ b/quarkus/runtime/src/main/resources/application.properties @@ -110,5 +110,4 @@ quarkus.log.category."io.smallrye.openapi.runtime.scanner.dataobject".level=off quarkus.log.category."org.hibernate.orm.sql.exec".level=info quarkus.shutdown.delay=1s # Graceful shutdown defaults -quarkus.shutdown.delay-enabled=true -quarkus.shutdown.timeout=1s \ No newline at end of file +quarkus.shutdown.delay-enabled=true \ No newline at end of file diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelp.approved.txt index 151469aca79d..7417a88bfc64 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelp.approved.txt @@ -282,6 +282,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt index 98a444911b0f..3e082cbb54f3 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartDevHelpAll.approved.txt @@ -361,6 +361,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelp.approved.txt index dd5e61397f31..d76b95875819 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelp.approved.txt @@ -330,6 +330,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt index 5e2b35a87779..c0fb3e589631 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartHelpAll.approved.txt @@ -362,6 +362,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelp.approved.txt index e6c8902a7a6d..144fd1f7b0fc 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelp.approved.txt @@ -292,6 +292,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt index cd6540665c68..801be8a9fae5 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testStartOptimizedHelpAll.approved.txt @@ -324,6 +324,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelp.approved.txt index a830db025a46..22d66532ba1a 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelp.approved.txt @@ -329,6 +329,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt index 4af617fc23b9..a73a1e623f2d 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityCheckHelpAll.approved.txt @@ -361,6 +361,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelp.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelp.approved.txt index c3def1c2d9c2..96a2c34ff1c4 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelp.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelp.approved.txt @@ -327,6 +327,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: diff --git a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt index d297cc5dd15f..fb2670ff4fbd 100644 --- a/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt +++ b/quarkus/tests/integration/src/test/resources/org/keycloak/it/cli/dist/approvals/cli/help/HelpCommandDistTest.testUpdateCompatibilityMetadataHelpAll.approved.txt @@ -359,6 +359,16 @@ HTTP(S): The type of the trust store file. If not given, the type is automatically detected based on the file extension. If 'fips-mode' is set to 'strict' and no value is set, it defaults to 'BCFKS'. +--shutdown-delay + Length of the pre-shutdown phase during which the server prepares for + shutdown. May be an ISO 8601 duration value, an integer number of seconds, + or an integer followed by one of [ms, h, m, s, d]. This period allows for + loadbalancer reconfiguration and draining of TLS/HTTP keepalive connections. + Default: 1s. +--shutdown-timeout + The shutdown period waiting for currently running HTTP requests to finish. May + be an ISO 8601 duration value, an integer number of seconds, or an integer + followed by one of [ms, h, m, s, d]. Default: 1s. HTTP Access log: From 6ea36b1056849c99bfdefda7cf104c377fc82fea Mon Sep 17 00:00:00 2001 From: Ruchika Date: Thu, 12 Feb 2026 10:01:04 +0000 Subject: [PATCH 3/9] Fixed PR review comments Closes #45833 Signed-off-by: Ruchika --- docs/guides/server/reverseproxy.adoc | 13 +++++++------ .../configuration/mappers/HttpPropertyMappers.java | 5 ++++- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/guides/server/reverseproxy.adoc b/docs/guides/server/reverseproxy.adoc index 9895f9c3dc5d..7a02a8bf56a3 100644 --- a/docs/guides/server/reverseproxy.adoc +++ b/docs/guides/server/reverseproxy.adoc @@ -6,7 +6,8 @@ <@tmpl.guide title="Configuring a reverse proxy" summary="Configure {project_name} with a reverse proxy, API gateway, or load balancer." -includedOptions="proxy-* hostname hostname-admin http-relative-path"> + +includedOptions="proxy-* hostname hostname-admin http-relative-path shutdown-delay shutdown-timeout"> Distributed environments frequently require the use of a reverse proxy. {project_name} offers several options to securely integrate with such environments. @@ -254,8 +255,6 @@ The NGINX SSL/TLS module does not expose the client certificate chain. {project_ If you are using this provider, see <@links.server id="keycloak-truststore"/> for how to configure a {project_name} Truststore. - - === Configuring the rfc9440 provider If you stick to the header names mentioned in RFC 9440, you do not need to configure any additional options after selecting the `rfc9440` provider. @@ -374,21 +373,21 @@ For production with TLS passthrough: [source,bash] ---- -bin/kc.sh start --shutdown-delay=5s --shutdown-timeout=30s +<@kc.start parameters="--shutdown-delay=5s --shutdown-timeout=30s"/> ---- For load balancers that poll readiness: [source,bash] ---- -bin/kc.sh start --shutdown-delay=10s --shutdown-timeout=10s +<@kc.start parameters="-shutdown-delay=10s --shutdown-timeout=10s"/> ---- For test environments: [source,bash] ---- -bin/kc.sh start-dev --shutdown-delay=0s --shutdown-timeout=500ms +<@kc.start parameters="-shutdown-delay=0s --shutdown-timeout=500ms"/> ---- [NOTE] @@ -396,3 +395,5 @@ bin/kc.sh start-dev --shutdown-delay=0s --shutdown-timeout=500ms The shutdown delay affects the minimum time required for a complete server restart. In Kubernetes environments, ensure your `terminationGracePeriodSeconds` is longer than the sum of `shutdown-delay` and `shutdown-timeout` to prevent forced termination. ==== + + diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java index 36da54a45abf..73d1a0ff10b2 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java @@ -259,11 +259,14 @@ private static String resolveMaxThreads(String value, private static void validateDuration(String value) { try { Duration duration = DurationConverter.parseDuration(value); + if (duration.isNegative() || duration.isZero()) { + throw new PropertyException("Invalid duration '" + value + "'. Duration must be positive."); + } if (duration.compareTo(Duration.ofSeconds(1)) < 0) { throw new PropertyException("Duration must be at least 1 second."); } } catch (IllegalArgumentException e) { - throw new PropertyException("Invalid duration format. Expected format examples: 1s, 30s, 1m, etc."); + throw new PropertyException("Invalid duration format '" + value + "'. May be an ISO 8601 duration value, an integer number of seconds, or an integer followed by one of [ms, h, m, s, d]."); } } } From 428422e3cea44a42b8701a45a8dadd03661c2d3d Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Thu, 12 Feb 2026 17:00:01 +0100 Subject: [PATCH 4/9] Adding additional check Signed-off-by: Alexander Schwartz --- docs/guides/observability/health.adoc | 10 +++++++++- .../java/org/keycloak/it/cli/dist/HealthDistTest.java | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/guides/observability/health.adoc b/docs/guides/observability/health.adoc index 2b3368b6cc0d..4221eca7a402 100644 --- a/docs/guides/observability/health.adoc +++ b/docs/guides/observability/health.adoc @@ -40,6 +40,10 @@ These endpoints respond with HTTP status `200 OK` on success or `503 Service Una { "status": "UP", "checks": [ + { + "name": "Graceful Shutdown", + "status": "UP" + }, { "name": "Keycloak cluster health check", "status": "UP" @@ -120,9 +124,13 @@ The table below shows the available checks. |Returns the status of the cluster (network partitions). |No +|Graceful shutdown +|Will start to return "DOWN" once the pre-shutdown phase started. +|No + |=== -For some checks, you'll need to also enable metrics as indicated by the `Requires Metrics` column. To enable metrics +For some checks, you'll need to also enable metrics as indicated by the *Requires Metrics* column. To enable metrics use the `metrics-enabled` option as follows: <@kc.build parameters="--health-enabled=true --metrics-enabled=true"/> diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java index 2ed562d9e804..9200e3aceec6 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HealthDistTest.java @@ -83,7 +83,7 @@ void testNonBlockingProbes() { .statusCode(200); when().get("/health/ready").then() .statusCode(200) - .body("checks.size()", equalTo(2)); + .body("checks.size()", equalTo(3)); when().get("/lb-check").then() .statusCode(404); } From 89a0311c90e828d83c0ec2c7969186c8d4047d65 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Thu, 12 Feb 2026 17:37:23 +0100 Subject: [PATCH 5/9] Docs review Signed-off-by: Alexander Schwartz --- docs/guides/server/reverseproxy.adoc | 52 +++++++++++++++++----------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/docs/guides/server/reverseproxy.adoc b/docs/guides/server/reverseproxy.adoc index 7a02a8bf56a3..c669112a0db7 100644 --- a/docs/guides/server/reverseproxy.adoc +++ b/docs/guides/server/reverseproxy.adoc @@ -54,7 +54,7 @@ By default {project_name} is exposed through the root context path (`/`). If the - Use a simple hostname for the `hostname` option, `xforwarded` for the `proxy-headers` option, and have the proxy set the `X-Forwarded-Prefix` header. - Use a full URL for the `hostname` option including the proxy context path, for example using `--hostname=https://my.keycloak.org/auth` if {project_name} is exposed through the reverse proxy on `/auth`. - Change the context path of {project_name} itself to match the context path for the reverse proxy using the `http-relative-path` option. - + For more details on exposing {project_name} on different hostname or context path incl. Administration REST API and Console, see <@links.server id="hostname"/>. == Enable sticky sessions @@ -329,40 +329,50 @@ Both values accept duration formats: `1s` (seconds), `500ms` (milliseconds), `2m === When to adjust shutdown timeouts -Consider adjusting these values based on your deployment configuration: +Consider adjusting these values based on your deployment configuration with the following example Scenarios: -[%header,cols="2,1,1,3"] +[%autowidth,cols="2,>1,>1,3a"] |=== |Scenario |Delay |Timeout |Reason |Load balancer polls readiness probe -|10s -|1-10s -|Allow 1-2 poll cycles (typically 5s intervals) for load balancer to detect shutdown +|16s +| +|Assumptions: + +* 5s poll interval and a load balancer reconfiguring after two successive failed probes. +* 1s for reconfiguring the proxy. +* Proxy using TLS re-encrypt or edge termination. + +Calculation: + +* Allow three poll cycles for the load balancer to detect shutdown and the extra time for the load balancer to perform the reconfiguration: ++ +`shutdown delay = 3 * 5s + 1s` |TLS passthrough configuration -|1s -|10-30s -|Longer timeout allows keepalive connections to drain naturally and receive connection close signals +|10‑30s +| +|Longer delay allows keepalive connections to drain naturally and receive connection close signals. |Long-running admin API requests -|1s -|10-30s -|Admin operations may take longer than typical user requests +| +|10‑30s +|Admin operations may take longer than typical user requests. |Test environments / quick restarts |0s |500ms -|Minimize shutdown time when graceful draining is not needed +|Minimize shutdown time when graceful draining is not needed. -|Deployment orchestration notifies proxy before pod termination +|Deployment orchestration reconfigures the proxy and drains connections before Pod termination |0s -|1-10s +| |No pre-shutdown delay needed if proxy is already reconfigured -|Combined: TLS passthrough + polled readiness -|10s -|30s +|Combined: TLS passthrough plus polled readiness +|26‑56s +| |Delays add up: time for load balancer detection + connection draining |=== @@ -373,21 +383,21 @@ For production with TLS passthrough: [source,bash] ---- -<@kc.start parameters="--shutdown-delay=5s --shutdown-timeout=30s"/> +<@kc.start parameters="--shutdown-delay=30s --shutdown-timeout=1s"/> ---- For load balancers that poll readiness: [source,bash] ---- -<@kc.start parameters="-shutdown-delay=10s --shutdown-timeout=10s"/> +<@kc.start parameters="--shutdown-delay=16s --shutdown-timeout=1s"/> ---- For test environments: [source,bash] ---- -<@kc.start parameters="-shutdown-delay=0s --shutdown-timeout=500ms"/> +<@kc.start parameters="--shutdown-delay=0s --shutdown-timeout=500ms"/> ---- [NOTE] From 9bbfca270dd8979a5ac682e8784c2ed73e3d6843 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Thu, 12 Feb 2026 17:54:30 +0100 Subject: [PATCH 6/9] Refinement Signed-off-by: Alexander Schwartz --- quarkus/runtime/src/main/resources/application.properties | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/quarkus/runtime/src/main/resources/application.properties b/quarkus/runtime/src/main/resources/application.properties index b632403411d4..5d8f46b4f491 100644 --- a/quarkus/runtime/src/main/resources/application.properties +++ b/quarkus/runtime/src/main/resources/application.properties @@ -108,6 +108,5 @@ mp.openapi.extensions.smallrye.duplicateOperationIdBehavior=FAIL quarkus.log.category."io.smallrye.openapi.runtime.scanner.dataobject".level=off # 3.31.1 appears to have a hibernate bug quarkus.log.category."org.hibernate.orm.sql.exec".level=info -quarkus.shutdown.delay=1s -# Graceful shutdown defaults +# Enable shutdown delay by default as it is a built-time property, and timeout are then configured at runtime quarkus.shutdown.delay-enabled=true \ No newline at end of file From 941ffd6aad133efba984ccfdb381fc8439b6582c Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Thu, 12 Feb 2026 17:59:35 +0100 Subject: [PATCH 7/9] Allow value of zero Signed-off-by: Alexander Schwartz --- .../configuration/mappers/HttpPropertyMappers.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java index 73d1a0ff10b2..2b8b891614d4 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java @@ -188,12 +188,12 @@ public List> getPropertyMappers() { fromOption(HttpOptions.SHUTDOWN_TIMEOUT) .to("quarkus.shutdown.timeout") .paramLabel("timeout") - .validator(HttpPropertyMappers::validateDuration) + .validator(HttpPropertyMappers::validateShutdownDuration) .build(), fromOption(HttpOptions.SHUTDOWN_DELAY) .to("quarkus.shutdown.delay") .paramLabel("delay") - .validator(HttpPropertyMappers::validateDuration) + .validator(HttpPropertyMappers::validateShutdownDuration) .build() ); @@ -256,14 +256,11 @@ private static String resolveMaxThreads(String value, return value; } - private static void validateDuration(String value) { + private static void validateShutdownDuration(String value) { try { Duration duration = DurationConverter.parseDuration(value); - if (duration.isNegative() || duration.isZero()) { - throw new PropertyException("Invalid duration '" + value + "'. Duration must be positive."); - } - if (duration.compareTo(Duration.ofSeconds(1)) < 0) { - throw new PropertyException("Duration must be at least 1 second."); + if (duration.isNegative()) { + throw new PropertyException("Invalid duration '" + value + "'. Duration must be zero or positive."); } } catch (IllegalArgumentException e) { throw new PropertyException("Invalid duration format '" + value + "'. May be an ISO 8601 duration value, an integer number of seconds, or an integer followed by one of [ms, h, m, s, d]."); From f5191d2ce0c344861939e6e5d5b596c5f0b21cad Mon Sep 17 00:00:00 2001 From: Ruchika Date: Fri, 13 Feb 2026 12:40:20 +0000 Subject: [PATCH 8/9] Fixed PR review comments: testcases for new http property Closes #45381 Signed-off-by: Ruchika --- .../configuration/mappers/HttpPropertyMappers.java | 7 ++++--- .../org/keycloak/it/cli/dist/HttpDistTest.java | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java index 2b8b891614d4..5dd98aa9440d 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/configuration/mappers/HttpPropertyMappers.java @@ -11,6 +11,7 @@ import org.keycloak.common.Profile; import org.keycloak.common.crypto.FipsMode; import org.keycloak.config.HttpOptions; +import org.keycloak.config.OptionsUtil; import org.keycloak.config.SecurityOptions; import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.Messages; @@ -259,11 +260,11 @@ private static String resolveMaxThreads(String value, private static void validateShutdownDuration(String value) { try { Duration duration = DurationConverter.parseDuration(value); - if (duration.isNegative()) { - throw new PropertyException("Invalid duration '" + value + "'. Duration must be zero or positive."); + if (duration == null || duration.isNegative()) { + throw new PropertyException("Invalid duration '%s'. Duration must be zero or positive.".formatted(value)); } } catch (IllegalArgumentException e) { - throw new PropertyException("Invalid duration format '" + value + "'. May be an ISO 8601 duration value, an integer number of seconds, or an integer followed by one of [ms, h, m, s, d]."); + throw new PropertyException("Invalid duration format '%s'. %s".formatted(value, OptionsUtil.DURATION_DESCRIPTION)); } } } diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HttpDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HttpDistTest.java index 6b64e77c8ada..43c1eacd3d0e 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HttpDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/HttpDistTest.java @@ -108,4 +108,18 @@ public void httpStoreTypeValidation(KeycloakDistribution dist) { result.assertExitCode(-1); result.assertMessage("ERROR: No trust store password provided"); } + + @Test + @Launch({"start-dev", "--shutdown-delay=1s", "--shutdown-timeout=0s"}) + public void testShutdownParametersValidValues() { + // Test that valid shutdown parameters are accepted (including 0s) + when().get("/realms/master").then().statusCode(200); + } + + @Test + public void testShutdownParametersNegativeValue(KeycloakDistribution dist) { + // Test that negative values are rejected + CLIResult result = dist.run("start-dev", "--shutdown-delay=-1s"); + result.assertError("Invalid duration '-1s'. Duration must be zero or positive"); + } } From ef58bae9fda005efadf042ea13cfa50a2d3ceaa5 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Fri, 13 Feb 2026 14:23:21 +0100 Subject: [PATCH 9/9] Adding release notes Signed-off-by: Alexander Schwartz --- .../documentation/release_notes/topics/26_6_0.adoc | 14 ++++++++++++++ .../tests/src/test/resources/ignored-links | 3 ++- .../upgrading/topics/changes/changes-26_6_0.adoc | 7 +++++++ docs/guides/server/reverseproxy.adoc | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/documentation/release_notes/topics/26_6_0.adoc b/docs/documentation/release_notes/topics/26_6_0.adoc index 75645ee3f599..4cdf687fa1ef 100644 --- a/docs/documentation/release_notes/topics/26_6_0.adoc +++ b/docs/documentation/release_notes/topics/26_6_0.adoc @@ -22,6 +22,20 @@ These properties are shared among the available OpenTelemetry components - logs, For more details, see the link:{telemetryguide_link}[{telemetryguide_name}] guide. += Graceful shutdown of HTTP stack + +To allow for rolling updates for configuration changes or version updates, a graceful shutdown of {project_name} nodes prevents users seeing error responses when logging in or refreshing their tokens when nodes shut down. + +Starting with this version, {project_name} supports a graceful shutdown of the HTTP stack. +This includes delaying a shutdown after receiving a termination signal, connection draining for HTTP/1.1 and HTTP/2 connections during that period, and a shutdown timeout to finish ongoing requests. + +The defaults are a shutdown delay and a shutdown timeout of one second each. +This should be a good fit for setups where the reverse proxy is using TLS edge termination or re-encrypt, and the reverse proxy is notified about the Keycloak node shutting down at the same time as the Keycloak node. +This is a common setup for example in Kubernetes environments. + +Users should adjust those values depending on their proxy setup. +See the section https://www.keycloak.org/server/reverseproxy#graceful-http-shutdown[Graceful HTTP shutdown] in the reverse proxy guide for more information. + = Custom request headers for OpenTelemetry It is now possible to set request headers for exporting telemetry via OpenTelemetry Protocol (OTLP). diff --git a/docs/documentation/tests/src/test/resources/ignored-links b/docs/documentation/tests/src/test/resources/ignored-links index a9e255c65f4c..bb6f00ea9ae6 100644 --- a/docs/documentation/tests/src/test/resources/ignored-links +++ b/docs/documentation/tests/src/test/resources/ignored-links @@ -42,4 +42,5 @@ https://docs.kantarainitiative.org* https://saml.xml.org* # To be removed once KC 26.6.0 is released -https://www.keycloak.org/securing-apps/dpop \ No newline at end of file +https://www.keycloak.org/securing-apps/dpop +https://www.keycloak.org/server/reverseproxy#graceful-http-shutdown \ No newline at end of file diff --git a/docs/documentation/upgrading/topics/changes/changes-26_6_0.adoc b/docs/documentation/upgrading/topics/changes/changes-26_6_0.adoc index 7a0f0ba039d2..3bc9b25ecc78 100644 --- a/docs/documentation/upgrading/topics/changes/changes-26_6_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-26_6_0.adoc @@ -38,6 +38,13 @@ When running the server in dev mode on a platform other than Windows Subsystem F This ensures your dev instance won't be accessible from other machines. If you want the previous behavior of binding to all interfaces, then explicitly set `http-host` to `0.0.0.0`. +=== Graceful shutdown of HTTP stack + +Starting with this version, {project_name} will apply a shutdown delay and a shutdown timeout of one second each to allow a graceful termination. + +Users should adjust those values depending on their proxy setup. +See the section https://www.keycloak.org/server/reverseproxy#graceful-http-shutdown[Graceful HTTP shutdown] in the reverse proxy guide for more information. + === `X-Forwarded-Prefix` Header is now supported With `proxy-headers` set to `xforwarded`, the server can determine the proxy context path from the `X-Forwarded-Prefix` header. diff --git a/docs/guides/server/reverseproxy.adoc b/docs/guides/server/reverseproxy.adoc index c669112a0db7..5f1fa6de12a3 100644 --- a/docs/guides/server/reverseproxy.adoc +++ b/docs/guides/server/reverseproxy.adoc @@ -278,6 +278,7 @@ The default values of the options are as follows: If your certificate chain is longer than the given default, you must define the option with an appropriate number. Otherwise, the provider will discard the request. +[[graceful-http-shutdown]] == Graceful HTTP shutdown When running {project_name} behind a reverse proxy or load balancer, graceful shutdown ensures that in-flight requests complete successfully during server termination, preventing connection errors for clients.