Files
2026-05-21 16:08:34 +02:00

618 lines
29 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<#import "/templates/guide.adoc" as tmpl>
<#import "/templates/kc.adoc" as kc>
<#import "/templates/options.adoc" as opts>
<#import "/templates/links.adoc" as links>
<@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 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.
== TLS termination modes
When deploying {project_name} behind a reverse proxy, there are three common approaches for handling TLS connections between clients, the proxy, and {project_name}: re-encrypt, edge termination and passthrough.
=== Re-encrypt
For re-encrypt, the reverse proxy terminates the client TLS connection and establishes a TLS connection to {project_name}.
The proxy can inspect and modify HTTP headers (such as `Forwarded` or `X-Forwarded-*`) before re-encrypting the traffic to the backend.
The proxy and {project_name} use independent TLS certificates: the proxy presents its own certificate to clients, while {project_name} uses a separate certificate for backend communication.
Configure `--proxy-headers` so that {project_name} parses the forwarded headers set by the proxy, including the real client IP address.
=== Edge termination
The edge termination is similar to re-encrypt as the proxy terminates the TLS connection, but the connection between the proxy and {project_name} is then unencrypted.
As edge termination is generally considered less secure than re-encrypt, this {section} focuses on re-encrypt instead.
=== Passthrough
With passthrough, the reverse proxy forwards the raw TLS connection directly to {project_name} without terminating it.
The TLS handshake occurs between the client and {project_name}, so the proxy cannot inspect or modify HTTP traffic.
Do not set `--proxy-headers` because the proxy cannot inject forwarded headers into the encrypted traffic.
Enable the PROXY protocol (`--proxy-protocol-enabled=true`) so that {project_name} can see the real client IP address.
=== Comparison
The following table compares different aspects of the re-encrypt and passthrough proxy configurations.
[%autowidth]
|===
|Aspect |Re-encrypt |Passthrough
|TLS between client and proxy |Yes |Yes (end-to-end)
|TLS between proxy and {project_name} |Yes |N/A (direct tunnel)
|Proxy can modify HTTP headers |Yes |No
|Proxy can filter URL paths |Yes |No
|{project_name} manages TLS certificates |Yes |Yes
|Client certificate (X.509) forwarding |Via HTTP headers added by the proxy |Native (no header needed)
|===
=== When to use each mode
Re-encrypt is the most common choice for production deployments.
It allows the proxy to set forwarded headers, filter URL paths, and apply HTTP-level policies while maintaining TLS encryption on both sides of the proxy.
The proxy and {project_name} can use different TLS certificates, giving you flexibility in certificate management.
Because the proxy can inspect HTTP traffic, it can also pool backend connections, cache static resources, and use cookie-based session affinity.
Passthrough provides true end-to-end encryption without TLS termination at the proxy.
It is the recommended mode when using X.509 client certificate authentication because the client certificate reaches {project_name} directly without being forwarded through an HTTP header, which eliminates the risk of forged client certificate headers.
However, because the proxy cannot modify HTTP traffic, it cannot set forwarded headers or filter URL paths.
This mode also requires {project_name} to present a certificate that covers all its externally visible hostnames, which can complicate certificate management.
=== Security considerations
When using re-encrypt:
// TODO: When the proxy and Keycloak use mTLS, restricting network access might not be necessary, or that mTLS could be a restriction of the network access.
* Restrict network access so that {project_name} accepts connections only from the proxy.
Without this restriction, clients could bypass the proxy and send forged forwarded headers directly to {project_name}.
+
Set `--proxy-trusted-addresses` to the IP addresses of your proxy to ensure forwarded headers are accepted only from these IP addresses. Note that this is only weak protection because IP addresses can be spoofed.
* Ensure the proxy overwrites (not just appends to) forwarded headers to prevent clients from injecting false values.
When using passthrough:
* Restrict network access so that {project_name} accepts connections only from the proxy.
* Forwarded headers are not applicable because the proxy cannot modify the encrypted traffic.
* Enable the PROXY protocol (`--proxy-protocol-enabled=true`) to see the real client IP address.
Note that the PROXY protocol is not compatible with the `--proxy-headers` option.
+
The PROXY protocol header that the proxy adds to convey the client IP address is neither encrypted nor signed.
Therefore, it can be spoofed, tampered with, or leaked.
Mitigate this risk, for example, by restricting network access.
* Allow a longer `--shutdown-delay` (for example, 1030 seconds) to give keepalive connections time to drain during shutdown, since the proxy cannot signal a connection close at the HTTP connection level.
== Port to be proxied
{project_name} runs on the following ports by default:
* `8443` (`8080` when you enable HTTP explicitly by `--http-enabled=true`)
* `9000`
The port `8443` (or `8080` if HTTP is enabled) is used for the Admin UI, Account Console, SAML and OIDC endpoints, and the Admin REST API as described in the <@links.server id="hostname"/> {section}.
The port `9000` is used for management, which includes endpoints for health checks and metrics as described in the <@links.server id="management-interface"/> {section}.
You only need to proxy port `8443` (or `8080`) even when you use different hostnames for frontend/backend and administration as described in <@links.server id="configuration-production"/>.
You should not proxy port `9000` because health checks and metrics use that port directly, and you do not want to expose this information to external callers.
== {project_name} with TLS passthrough
In TLS passthrough mode, the proxy forwards the raw TLS connection to {project_name} without terminating it.
Because the proxy cannot inspect or modify HTTP traffic, it cannot set or overwrite HTTP headers such as `Forwarded` or `X-Forwarded-*`.
[WARNING]
====
Do not set `--proxy-headers` when using TLS passthrough.
The proxy cannot inject forwarded headers into the encrypted traffic, and any headers already present in the request originate from the client and cannot be trusted.
Setting `--proxy-headers` in this mode would allow clients to spoof their IP address or protocol by sending forged headers directly to {project_name}.
====
Without forwarded headers, the only way for {project_name} to see the real client IP address is through the PROXY protocol, which is strongly recommended for TLS passthrough deployments.
=== PROXY Protocol
The PROXY protocol allows the proxy to prepend the original client connection information (such as the source IP address) to the TCP stream before forwarding it to {project_name}.
This happens outside of the TLS-encrypted channel, so it works even though the proxy cannot modify HTTP traffic.
Enable it in {project_name} with the `--proxy-protocol-enabled` option:
<@kc.start parameters="--proxy-protocol-enabled true"/>
The proxy must also be configured to send the PROXY protocol header.
Most proxies support both version 1 (text-based) and version 2 (binary).
Consult your proxy's documentation for the specific configuration directive (for example, `send-proxy-v2` in HAProxy).
NOTE: The `--proxy-protocol-enabled` option and the `--proxy-headers` option are mutually exclusive.
Setting both will result in a configuration error.
=== Blueprints
* <@links.server id="haproxy-passthrough"/>
* <@links.server id="traefik-passthrough"/>
== {project_name} with TLS reencrypt
In TLS re-encrypt mode, the proxy terminates the client TLS connection and establishes a new TLS connection to {project_name}.
Because the proxy can inspect and modify HTTP traffic, additional configuration options are available, such as forwarded headers, path filtering, sticky sessions, and client certificate forwarding.
=== Configure the reverse proxy headers
{project_name} parses the reverse proxy headers based on the `proxy-headers` option, which accepts several values:
* `forwarded` enables parsing of the `Forwarded` header as per https://www.rfc-editor.org/rfc/rfc7239.html[RFC 7239].
* `xforwarded` enables parsing of non-standard `X-Forwarded-*` headers, such as `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, `X-Forwarded-Port`, and `X-Forwarded-Prefix`.
NOTE: If you are using a reverse proxy for anything other than TLS passthrough and do not set the `proxy-headers` option, then by default you will see 403 Forbidden responses to requests via the proxy that perform origin checking.
For example:
<@kc.start parameters="--proxy-headers forwarded"/>
WARNING: If either `forwarded` or `xforwarded` is selected, make sure your reverse proxy properly sets and overwrites the `Forwarded` or `X-Forwarded-*` headers respectively. To set these headers, consult the documentation for your reverse proxy. Do not use `forwarded` or `xforwarded` with TLS passthrough. Misconfiguration will leave {project_name} exposed to security vulnerabilities.
Take extra precautions to ensure that the client address is properly set by your reverse proxy via the `Forwarded` or `X-Forwarded-For` headers.
If these headers are incorrectly configured, rogue clients can inject false values and trick {project_name} into thinking the client is connecting from a different IP address than the actual one.
This precaution is especially critical if you do any deny or allow listing of IP addresses.
NOTE: When using the `xforwarded` setting, the `X-Forwarded-Port` takes precedence over any port included in the `X-Forwarded-Host`.
NOTE: If the TLS connection is terminated at the reverse proxy (edge termination), enabling HTTP through the `http-enabled` setting is required.
=== Trusted Proxies
To ensure that proxy headers are used only from proxies you trust, set the `proxy-trusted-addresses` option to a comma-separated list of IP addresses (IPv4 or IPv6) or Classless Inter-Domain Routing (CIDR) notations.
For example:
<@kc.start parameters="--proxy-headers forwarded --proxy-trusted-addresses=192.168.0.32,127.0.0.0/8"/>
[[header-filtering-recommendations]]
=== Header filtering recommendations
When using TLS re-encrypt, the proxy can inspect and modify HTTP traffic.
Use this capability to prevent external clients from injecting headers that affect identity resolution, access control, or observability.
NOTE: The following assumes that there is only a single proxy layer in front of {project_name}, and that it is not receiving traffic from another trusted proxy layer with trusted headers.
Configure the proxy to apply the following rules to incoming requests before forwarding them to {project_name}:
* *Overwrite* `Forwarded` and `+X-Forwarded-*+` headers with the proxy's own values rather than removing them, because {project_name} relies on these headers when `--proxy-headers` is configured.
* *Strip* all other headers listed below entirely.
[%autowidth]
|===
|Header(s) |Category |Risk if not filtered
|`Forwarded`, `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host`, `X-Forwarded-Port`, `X-Forwarded-Prefix`
|Proxy identity
|Clients can spoof their IP address, protocol, or host, affecting access control and audit logging.
*Overwrite* these headers rather than stripping them.
|`X-Original-Forwarded-For`
|Proxy identity
|Variant of `X-Forwarded-For` recognized by some proxies.
Can be spoofed to bypass IP-based access controls.
|`X-Real-IP`
|Proxy identity
|Trusted by some applications for rate limiting and audit logging.
Can be spoofed to bypass IP-based restrictions.
|`X-Original-URL`, `X-Original-Method`
|Proxy identity
|Used by authentication sub-request mechanisms in some proxies.
Can be spoofed to manipulate path-based authorization decisions.
|`X-Forwarded-Access-Token`
|Proxy identity
|Injected by some OAuth2 proxies.
Can be spoofed to inject forged access tokens.
|`traceparent`, `tracestate`
|Distributed tracing
|https://www.w3.org/TR/trace-context/[W3C Trace Context] headers.
External injection allows attackers to correlate requests in the tracing backend and map internal service dependencies.
|`baggage`
|Distributed tracing
|https://www.w3.org/TR/baggage/[W3C Baggage] header.
Can inject arbitrary key-value pairs into the trace context propagated to downstream services.
|`b3`, `x-b3-traceid`, `x-b3-spanid`, `x-b3-parentspanid`, `x-b3-sampled`, `x-b3-flags`
|Distributed tracing
|https://github.com/openzipkin/b3-propagation[Zipkin B3] propagation headers.
External injection inflates observability costs and enables cross-service correlation.
|`uber-trace-id`
|Distributed tracing
|https://www.jaegertracing.io/sdk-migration/#propagation-format[Jaeger] propagation header (deprecated in favor of W3C Trace Context).
Same risks as other tracing headers.
|`x-ot-span-context`
|Distributed tracing
|OpenTracing propagation header (deprecated in favor of W3C Trace Context).
Same risks as other tracing headers.
|===
=== Exposed path recommendations
When using a reverse proxy, {project_name} only requires certain paths to be exposed.
The following table shows the recommended paths to expose.
[%autowidth]
|===
|{project_name} Path|Reverse Proxy Path|Exposed|Reason
|/
|-
|No
|When exposing all paths, admin paths are exposed unnecessarily.
|/admin/
| -
|No
|Exposed admin paths lead to an unnecessary attack vector.
|/realms/
|/realms/
|Yes
|This path is needed to work correctly, for example, for OIDC endpoints.
|/resources/
|/resources/
|Yes
|This path is needed to serve assets correctly. It may be served from a CDN instead of the {project_name} path.
|/.well-known/
|/.well-known/
|Yes
|This path is needed to resolve Authorization Server Metadata and other information via https://www.rfc-editor.org/rfc/rfc8414.html[RFC 8414].
|/metrics
|-
|No
|Exposed metrics lead to an unnecessary attack vector.
|/health
|-
|No
|Exposed health checks lead to an unnecessary attack vector.
|===
We assume you run {project_name} on the root path `/` on your reverse proxy/gateway's public API.
If not, prefix the path with your desired one.
NOTE: If you configured an `http-relative-path` on the server, proceed as follows to use discovery with RFC 8414: Configure a reverse proxy to map the `/.well-known/` path without the prefix to the path with the prefix on the server.
=== Different context path on reverse proxy
By default, {project_name} is exposed through the root context path (`/`).
If the proxy uses a different context path than {project_name}, one of the following must be done:
* 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 a different hostname or context path, including the Administration REST API and Console, see <@links.server id="hostname"/>.
=== Enable sticky sessions
A typical cluster deployment consists of a load balancer (reverse proxy) and two or more {project_name} servers on a private network.
For performance purposes, it may be useful if the load balancer forwards all requests related to a particular browser session to the same {project_name} backend node.
The reason is that {project_name} uses an Infinispan distributed cache internally to store data related to the current authentication session and user session.
The Infinispan distributed caches are configured with a limited number of owners.
That means that session-related data is stored only on some cluster nodes, and the other nodes need to look up the data remotely if they want to access it.
For example, if an authentication session with ID 123 is saved in the Infinispan cache on node1, and then node2 needs to look up this session, it needs to send the request to node1 over the network to retrieve the session entity.
It is beneficial if a particular session entity is always available locally, which can be achieved with sticky sessions.
The workflow in a cluster environment with a public frontend load balancer and two backend {project_name} nodes can be as follows:
* The user sends the initial request to see the {project_name} login screen.
* This request is served by the frontend load balancer, which forwards it to some random node (e.g., node1).
Strictly speaking, the node does not need to be random, but can be chosen according to other criteria (client IP address, etc.).
It all depends on the implementation and configuration of the underlying load balancer (reverse proxy).
* {project_name} creates an authentication session with a random ID (e.g., 123) and saves it to the Infinispan cache.
* The Infinispan distributed cache assigns the primary owner of the session based on the hash of the session ID.
See the Infinispan documentation for more details.
Assume that Infinispan assigns node2 as the owner of this session.
* {project_name} creates the cookie `AUTH_SESSION_ID` with the format `<session-id>.<owner-node-id>`.
In this example, the value is `123.node2`.
* The response is returned to the user with the {project_name} login screen and the `AUTH_SESSION_ID` cookie in the browser.
From this point, it is beneficial if the load balancer forwards all subsequent requests to node2, as this is the node that owns the authentication session with ID 123 and hence Infinispan can look up this session locally.
After authentication is finished, the authentication session is converted to a user session, which is also saved on node2 because it has the same ID 123.
Sticky sessions are not mandatory for a cluster setup; however, they improve performance for the reasons mentioned above.
You need to configure your load balancer to use the `AUTH_SESSION_ID` cookie for session affinity.
The appropriate procedure depends on your load balancer.
If your proxy supports session affinity without processing cookies from backend nodes, you should set the `+spi-sticky-session-encoder--infinispan--should-attach-route+` option to `false` in order to avoid attaching the node to cookies and just rely on the reverse proxy capabilities.
<@kc.start parameters="--spi-sticky-session-encoder--infinispan--should-attach-route=false"/>
By default, the `+spi-sticky-session-encoder--infinispan--should-attach-route+` option value is `true` so that the node name is attached to cookies to indicate to the reverse proxy the node that subsequent requests should be sent to.
=== Enabling client certificate lookup
When the proxy is configured as a TLS termination proxy, the client certificate information can be forwarded to the server through specific HTTP request headers and then used to authenticate clients.
You can configure how the server retrieves client certificate information depending on the proxy you are using.
[WARNING]
====
Client certificate lookup via a proxy header for X.509 authentication is considered security-sensitive. If misconfigured, a forged client certificate header can be used for authentication.
*Extra precautions need to be taken to ensure that the client certificate information can be trusted when passed via a proxy header.*
* Double-check that your use case needs re-encrypt or edge TLS termination, which implies using a proxy header for client certificate lookup.
TLS passthrough is recommended as a more secure option when X.509 authentication is desired because it does not require passing the certificate via a proxy header.
Client certificate lookup from a proxy header is applicable only to re-encrypt
and edge TLS termination.
* If passthrough is not an option, implement the following security measures:
** Configure your network so that {project_name} is isolated and can accept connections only from the proxy.
** Make sure that the proxy overwrites the header that is configured in the `spi-x509cert-lookup--<provider>--ssl-client-cert` option.
** Pay extra attention to the `spi-x509cert-lookup--<provider>--trust-proxy-verification` setting. Make sure you enable it only if you can trust your proxy to verify the client certificate.
Setting `spi-x509cert-lookup--<provider>--trust-proxy-verification=true` without the proxy verifying the client certificate chain will expose {project_name} to a security vulnerability where a forged client certificate can be used for authentication.
====
The server supports some of the most common TLS termination proxies:
[%autowidth]
|===
|Provider|Proxies
|apache
|Apache HTTP Server
|haproxy
|HAProxy
|nginx
|NGINX
|traefik
|Traefik (PassTLSClientCert middleware with `pem: true`)
|rfc9440
|Proxies compliant with link:https://datatracker.ietf.org/doc/rfc9440/[RFC 9440]
|envoy
|Envoy
|===
To configure how client certificates are retrieved from the requests, you need to:
.Enable the corresponding proxy provider
<@kc.build parameters="--spi-x509cert-lookup--provider=<provider>"/>
.Configure the HTTP headers
<@kc.start parameters="--spi-x509cert-lookup--<provider>--ssl-client-cert=SSL_CLIENT_CERT --spi-x509cert-lookup--<provider>--ssl-cert-chain-prefix=CERT_CHAIN --spi-x509cert-lookup--<provider>-certificate-chain-length=10"/>
When configuring the HTTP headers, you need to make sure the values you are using correspond to the names of the headers
forwarded by the proxy with the client certificate information.
Common options for configuring providers are:
[%autowidth]
|===
|Option|Description|Supporting Providers
|ssl-client-cert
| The name of the header holding the client certificate
| all but `traefik` and `envoy`, optional for `rfc9440`
|ssl-cert-chain-prefix
| The prefix of the headers holding additional certificates in the chain and used to retrieve individual
certificates according to the length of the chain. For instance, a value `CERT_CHAIN` will tell the server
to load additional certificates from headers `CERT_CHAIN_0` to `CERT_CHAIN_9` if `certificate-chain-length` is set to `10`.
| `apache`, `haproxy`, `nginx`
|certificate-chain-length
| The maximum length of the certificate chain beyond the client certificate
| all but `envoy` (defaults to 1)
|===
==== Configuring the NGINX provider
The NGINX SSL/TLS module does not expose the client certificate chain. {project_name}'s NGINX certificate lookup provider rebuilds it by using the {project_name} truststore.
If you are using this provider, see <@links.server id="keycloak-truststore"/> for how
to configure a {project_name} Truststore. The options and defaults specific to `nginx` are as follows:
[%autowidth]
|===
|Option|Description|Default
|trust-proxy-verification
| Enable trusting NGINX proxy certificate verification, instead of forwarding the certificate to {project_name} and verifying it in {project_name}.
| false
|cert-is-url-encoded
| Whether the forwarded certificate is URL-encoded or not. In NGINX, this corresponds to the `$ssl_client_cert` and `$ssl_client_escaped_cert` variables.
| true
|===
==== 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.
The options and defaults specific to `rfc9440` are as follows:
[%autowidth]
|===
|Option|Description|Default
|ssl-client-cert
| The name of the header holding the client certificate
| Client-Cert
|ssl-cert-chain
| The name of the header holding additional certificates in the chain. This is not a prefix but the full name of the header
because RFC 9440 mandates that the chain certificates are contained in one header.
| Client-Cert-Chain
|===
If your certificate chain is longer than the default, you must set the `certificate-chain-length` option to an appropriate value.
Otherwise, the provider will discard the request.
==== Configuring the Traefik provider
The Traefik provider handles certificates forwarded by Traefik's link:https://doc.traefik.io/traefik/middlewares/http/passtlsclientcert/[PassTLSClientCert middleware] with `pem: true`.
Traefik sends the client certificate and any intermediate CA certificates as PEM blocks in a single `X-Forwarded-Tls-Client-Cert` header, separated by commas.
The `traefik` provider parses all certificates from this header.
Other than possibly changing the `certificate-chain-length`, you do not need to configure additional options for the `traefik` provider.
==== Configuring the Envoy provider
The Envoy provider automatically retrieves the client certificate and optional certificate chain from the `x-forwarded-client-cert` header.
You do not need to configure additional options for the `envoy` provider.
=== Blueprints
* <@links.server id="haproxy-reencrypt"/>
* <@links.server id="traefik-reencrypt"/>
[[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.
{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} waits for in-flight HTTP requests to complete.
Once the HTTP requests are complete, it uses the remaining time of the shutdown timeout to wait for caches to rebalance to avoid possible data loss.
Once the timeout expires, the server shuts down regardless.
=== Default behavior
By default, {project_name} is configured with a 1-second pre-shutdown delay and a 10-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,bash]
----
bin/kc.sh start --shutdown-delay=<duration> --shutdown-timeout=<duration>
----
Available options:
`--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`
`--shutdown-timeout`::
The shutdown period waiting for currently running HTTP requests to finish.
Default: `10s`
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.
The following table shows example scenarios:
[%autowidth,cols="2,>1,>1,3a"]
|===
|Scenario |Delay |Timeout |Reason
|Load balancer polls readiness probe
|16s
|
|Assumptions:
* A 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
|1030s
|
|Longer delay allows keepalive connections to drain naturally and receive connection close signals.
|Long-running admin API requests
|
|1030s
|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 reconfigures the proxy and drains connections before Pod termination
|0s
|
|No pre-shutdown delay needed if proxy is already reconfigured
|Combined: TLS passthrough plus polled readiness
|2656s
|
|Delays add up: time for load balancer detection + connection draining
|===
==== Example configurations
For production with TLS passthrough:
<@kc.start parameters="--shutdown-delay=30s" />
For load balancers that poll readiness:
<@kc.start parameters="--shutdown-delay=16s"/>
For test environments:
<@kc.start parameters="--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.
====
</@tmpl.guide>