Kubernetes and OpenShift
|
This section documents Quarkus 3.39.x (current) with 3.33.x LTS as the production recommendation, on the Java 17+ baseline, as published at the official Quarkus guides — which are the reference these pages are written and verified against. Quarkus 4 raises the baseline to Java 21 (first release candidate planned for end of September 2026, see the Quarkus blog); readers upgrading past 3.39.x/3.33.x LTS should expect that change. This content was generated with the assistance of AI and should be verified against those official guides before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Quarkus treats "deploy this to a cluster" as a build-time concern rather than a set of YAML files someone hand writes and hopes stays in sync with the application. Adding the right extension turns application configuration already known at build time — ports, env vars, health checks, resource requests — directly into Kubernetes or OpenShift manifests, generated fresh on every build. This page covers the Kubernetes extension and the properties that shape its generated manifests, OpenShift-specific deployment mechanics, and the Fabric8 Kubernetes client for programmatic access to a cluster from application code. For the underlying Kubernetes concepts, see the Kubernetes section.
The Kubernetes extension
Adding io.quarkus:quarkus-kubernetes to a project:
./mvnw quarkus:add-extension -Dextensions=kubernetes
turns on manifest generation as part of the normal build. Running ./mvnw package writes a kubernetes.yml (and,
once the OpenShift extension is also present, an openshift.yml) under target/kubernetes/, derived from the
application’s own configuration rather than authored separately. Nothing needs to be deployed for this to happen — the manifest is a build artifact like the JAR itself, meant to be inspected, versioned, or applied with
kubectl apply -f target/kubernetes/kubernetes.yml whenever the operator chooses to.
Shaping the generated manifest
The generated resources — a Deployment, a Service, and optionally an Ingress — are controlled through
application.properties, not through editing the generated YAML directly (a hand edit is overwritten on the next
build):
| Property | Purpose |
|---|---|
|
Adds labels to the generated resources, e.g. |
|
Adds annotations to the generated resources. |
|
Adds environment variables to the container spec, e.g. |
|
Declares additional container ports beyond the default HTTP port Quarkus already knows about. |
|
Sets CPU/memory requests and limits, e.g. |
|
Selects the |
|
Set to |
Liveness and readiness probes are generated automatically, wired to the application’s own health endpoints, when
the quarkus-smallrye-health extension is present on the classpath — the Kubernetes extension detects it and
adds the probes without any further configuration. See Observability: Health, Logging, Metrics, Tracing for the health
extension itself and what it checks.
One-step deploy
Setting quarkus.kubernetes.deploy=true makes the build itself apply the generated manifest to whatever cluster
the local kubectl context points at, immediately after the container image is built and pushed:
./mvnw package -Dquarkus.kubernetes.deploy=true
This folds "build image, push image, apply manifest" into a single command during iterative development; it is generally not how a manifest reaches a shared or production cluster, where a GitOps pipeline or a CI job applying a reviewed manifest is the norm.
Knative
Setting quarkus.kubernetes.deployment-target=knative switches manifest generation to produce a knative.yml
(a Knative Service) instead of, or alongside, the plain Kubernetes resources, targeting a cluster with Knative
Serving installed for scale-to-zero, request-driven autoscaling.
Init tasks and jobs
Some workloads need something to run once, to completion, before the application container starts serving
traffic — a database migration is the standard example. Quarkus' Kubernetes extension has an init-task mechanism
for this: an extension that needs a one-off step (for instance, a migration tool) can register an init task, and
the Kubernetes extension turns it into an init container on the generated Deployment, run to completion before
the main container starts, backed by properties under the quarkus.kubernetes.init-task-defaults.* namespace for
tuning the defaults applied to every init task. The exact set of per-task property names has moved between
Quarkus versions as the mechanism matured, so rather than repeat a specific list here that could be stale by the
time it’s read, check the current
Deploying to Kubernetes guide for the property names that
apply to the Quarkus version in use.
OpenShift specifics
Adding io.quarkus:quarkus-openshift alongside (or instead of) the plain Kubernetes extension generates an
openshift.yml tailored to OpenShift’s own conventions and defaults (routes instead of ingresses, OpenShift’s
build objects, and so on).
DeploymentConfig vs. Deployment
Older OpenShift versions centered on DeploymentConfig, an OpenShift-specific resource that predates
Kubernetes' own Deployment and adds OpenShift-only rollout triggers. Modern OpenShift fully supports the
standard Kubernetes Deployment, and current Quarkus defaults to generating one rather than a
DeploymentConfig. The choice is explicit and overridable:
quarkus.openshift.deployment-kind=Deployment
# or
quarkus.openshift.deployment-kind=DeploymentConfig
DeploymentConfig remains relevant mainly for older clusters or environments that still rely on its
OpenShift-specific triggers; new projects targeting current OpenShift should generally leave the default in
place.
Build strategy: S2I vs. Docker
OpenShift’s native build mechanism is Source-to-Image (S2I): a builder image plus the application’s source (or build output) are handed to the OpenShift build system, which produces the final container image inside the cluster itself, without a Dockerfile or an external image build step. Quarkus' OpenShift extension supports this as its default build strategy, and also supports a plain Docker/Dockerfile-based build as an alternative, selected with:
quarkus.openshift.build-strategy=s2i
# or
quarkus.openshift.build-strategy=docker
S2I keeps the whole build inside the cluster (useful where the CI environment has no separate image registry
access or Docker daemon of its own), while the docker strategy hands OpenShift a Dockerfile-driven build,
closer to what a non-OpenShift Kubernetes pipeline would do. For Container Images
this page covers image-building extensions in general; S2I is specifically the OpenShift-native option among
them.
Native executables build on OpenShift the same way, through an S2I builder image designed for native
compilation — the builder itself runs the GraalVM native-image step inside the cluster, so the local machine
triggering the build does not need GraalVM installed at all.
Reading ConfigMaps and Secrets at runtime: quarkus-kubernetes-config
The quarkus-kubernetes and quarkus-openshift extensions above generate manifests at build time; a separate
extension, quarkus-kubernetes-config, does the opposite direction — it lets the running application read
Kubernetes `ConfigMap`s and `Secret`s as Quarkus configuration sources, resolved at runtime against the cluster
the pod is actually running in:
quarkus.kubernetes-config.config-maps=my-app-config
quarkus.kubernetes-config.secrets=my-app-secrets
Values found in the named ConfigMap`s and `Secret`s are folded into the same configuration model as any other
source, so they can be injected the same way as values from `application.properties or environment variables.
See Configuration for how Quarkus configuration sources, @ConfigMapping, and
injection work in general — this extension only adds where some of the values come from.
The Fabric8 Kubernetes client
Beyond generating manifests, application code sometimes needs to talk to the cluster directly — list pods,
watch resources, create objects. The quarkus-kubernetes-client extension wires the Fabric8
io.fabric8.kubernetes.client.KubernetesClient into CDI, configured automatically from the in-cluster service
account when running inside a pod, or from the local kubeconfig otherwise:
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.client.KubernetesClient;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.util.List;
@ApplicationScoped
public class PodLister {
@Inject
KubernetesClient client;
public List<String> listPodNames(String namespace) {
return client.pods()
.inNamespace(namespace)
.list()
.getItems()
.stream()
.map(pod -> pod.getMetadata().getName())
.toList();
}
}
Testing against a mock server
Fabric8 ships a mock Kubernetes API server that the client extension integrates with for tests, so cluster
interaction can be exercised without a real cluster: annotating a test with the extension’s Kubernetes
test-server support (@KubernetesTestServer on a JUnit test, or the @WithKubernetesTestServer mechanism
depending on the Quarkus version) starts an in-process fake API server, pre-populated with whatever fixture
resources the test needs, and points the injected KubernetesClient at it instead of a live cluster. This keeps
tests fast and deterministic while still exercising the real Fabric8 client code paths.
Writing operators with the Operator SDK extension
For applications that go further than reading the cluster and instead reconcile custom resources — watching
for changes to a CRD and driving cluster state back toward a desired spec, the pattern behind a Kubernetes
operator — the quarkus-operator-sdk extension integrates the Java Operator SDK into Quarkus, letting a
reconciler be written as a CDI bean annotated to watch a given custom resource type, with the Kubernetes client,
manifest generation (including the CRD itself), and native-image support already wired together. It is a
substantial enough topic on its own that it is only introduced here; the
Operator SDK guide covers writing a full reconciler. The generic
concepts are in Custom Resources and Operators.
Dev Services for Kubernetes
When the quarkus-kubernetes-client extension is on the classpath and no target cluster is otherwise
configured, Quarkus Dev Services can spin up an ephemeral local cluster (backed by a tool such as kind) for dev
mode and tests automatically, the same pattern Dev Services use for databases and brokers elsewhere in Quarkus:
no manual cluster setup is needed to exercise Kubernetes-client code locally, and the ephemeral cluster is torn
down when the dev-mode session or test run ends.
From source to running pod
The pieces above compose into one pipeline: a build produces a container image, the Kubernetes/OpenShift extension generates a manifest referencing that image, and applying the manifest brings the workload up with its health probes already wired in.
See Container Images for how the container image itself is built and pushed before manifest generation picks it up. For deploying this to Azure’s managed Kubernetes service specifically, see Deploying Microservices on AKS. For AWS’s managed Kubernetes service, see Deploying Microservices on EKS. For Google Kubernetes Engine, see Deploying Microservices on GKE.