Tenant context propagation
This guide describes how TenantContext propagates within an active HTTP request and how tenant identity is carried or rebound when work leaves that request boundary.
Propagation model
TenantContext is request scoped. Tenant resolution establishes the current tenant at an application boundary, and downstream code reads the same tenant from TenantContext while that request context remains active.
There are two different propagation cases:
-
Within an active HTTP request, Quarkus REST, Vert.x duplicated context, and SmallRye Context Propagation preserve the request-scoped
TenantContextacross supported reactive and worker-thread boundaries automatically. -
After leaving the HTTP request, use the mechanism designed for the new boundary, such as
TenantContextRunnerfor synchronous background work or the Kafka module for Kafka messaging.
The extension provides explicit support for three common boundaries:
-
HTTP requests resolve a tenant from the configured resolver chain.
-
Synchronous background work can bind a tenant explicitly with
TenantContextRunner. -
Kafka messages can carry the tenant in a Kafka record header and restore it for the consumer handler.
Tenant context should not be treated as a global or ordinary thread-local value. The important distinction is whether work is still executing with the request’s propagated Quarkus context or has crossed into a boundary that requires explicit tenant propagation.
Reactive work within an HTTP request
No additional multitenancy propagation mechanism is required when Quarkus keeps asynchronous work inside the same HTTP request context.
The resolved tenant remains available automatically across supported Quarkus REST boundaries, including:
-
Mutiny
Unipipelines, including supported worker-pool thread switches such asemitOn. -
@Blockingendpoints and Quarkus worker-thread offload. -
CompletionStagework executed through a MicroProfileManagedExecutor.
This behavior is provided by Quarkus REST, Vert.x, and SmallRye Context Propagation; it is not a separate propagation implementation in this extension. A REST endpoint returning a Uni or a context-aware CompletionStage does not need TenantContextRunner merely because the result is asynchronous.
A raw executor that application code submits to directly is different: it does not automatically carry the request’s duplicated context. In that case, either use a context-aware Quarkus mechanism or capture the tenant id while the request context is active and pass the plain value explicitly.
See Reactive and asynchronous tenant propagation for the detailed boundary table, examples, failure/cancellation behavior, and the ManagedExecutor contract. Keeping those details in the main guide avoids duplicating two independent versions of the same Quarkus runtime behavior.
Synchronous background work
Use TenantContextRunner for scheduled jobs, startup observers, maintenance tasks, or other synchronous work that is not entered through the HTTP tenant-resolution pipeline.
import jakarta.inject.Inject;
import io.quarkiverse.multitenancy.core.runtime.context.TenantContextRunner;
import io.quarkus.scheduler.Scheduled;
public class TenantJob {
@Inject
TenantContextRunner tenantRunner;
@Scheduled(every = "1h")
void refreshTenantData() {
tenantRunner.runAsTenant("acme", this::refreshData);
}
void refreshData() {
// TenantContext contains "acme" here.
}
}
runAsTenant accepts a Runnable or Supplier<T>. If no CDI request context is active, the runner activates one for the duration of the callback. If a request context is already active, it reuses it. In both cases the previous tenant is restored when the callback completes or throws, which makes nested and sequential invocations safe.
Asynchronous boundary
TenantContextRunner deliberately supports synchronous work only. A Supplier that returns a CompletionStage or Mutiny Uni is rejected with IllegalStateException.
Do not use this pattern:
tenantRunner.runAsTenant("acme", () -> remoteCallReturningUni());
The callback can outlive the temporary tenant binding. This restriction applies to asynchronous work started from the temporary background binding; it does not mean reactive work inside an active Quarkus REST request needs manual tenant propagation. For background asynchronous work, use the context-propagation mechanism appropriate to the framework executing that work.
Kafka tenant propagation
The optional Kafka module carries the current tenant in a Kafka record header on outgoing messages and restores it to TenantContext before an incoming application handler runs. It applies to SmallRye Reactive Messaging channels that use the Kafka connector.
See Kafka tenant propagation for installation, an end-to-end HTTP-to-Kafka flow, incoming and outgoing behavior, strict missing-tenant policies, validation, custom validators, failure handling, and the complete configuration reference.
Choosing the right propagation mechanism
| Boundary | Recommended mechanism |
|---|---|
Incoming HTTP request |
Configure the HTTP resolver chain ( |
Reactive/worker work inside the same HTTP request |
No multitenancy-specific mechanism is required for supported Quarkus boundaries such as Mutiny |
Synchronous scheduled/background callback |
Wrap the callback with |
Asynchronous work started after leaving the request or a temporary background binding |
Do not rely on |
Raw executor submitted to directly from request code |
The request context is not propagated automatically; use a context-aware executor/mechanism or capture and pass the tenant id explicitly. |
Outgoing Kafka message |
Install the Kafka module; the current tenant is stamped into Kafka record metadata automatically. |
Incoming Kafka message |
Install the Kafka module and enable |
Operational guidance
Treat tenant identifiers received from HTTP or Kafka as untrusted input and keep validation enabled unless the application’s identifier format requires a wider policy.
For tenant-sensitive Kafka workloads, see Kafka tenant propagation for strict missing-tenant handling, validation, nack behavior, and failure-strategy guidance.
Keep the tenant boundary explicit when moving work outside a request. Supported Quarkus REST reactive and worker boundaries preserve the active request context automatically; TenantContextRunner is intended for synchronous work that needs a temporary tenant binding, while Kafka propagation is intended for Kafka connector boundaries. None of these mechanisms turns TenantContext into a global context store.