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

quarkus.kubernetes.labels.*

Adds labels to the generated resources, e.g. quarkus.kubernetes.labels.team=payments.

quarkus.kubernetes.annotations.*

Adds annotations to the generated resources.

quarkus.kubernetes.env.vars.*

Adds environment variables to the container spec, e.g. quarkus.kubernetes.env.vars.log-level=DEBUG.

quarkus.kubernetes.ports.*

Declares additional container ports beyond the default HTTP port Quarkus already knows about.

quarkus.kubernetes.resources.requests. / quarkus.kubernetes.resources.limits.

Sets CPU/memory requests and limits, e.g. quarkus.kubernetes.resources.requests.memory=256Mi.

quarkus.kubernetes.service-type

Selects the Service type: ClusterIP (default), NodePort, or LoadBalancer.

quarkus.kubernetes.ingress.expose

Set to true to also generate an Ingress resource exposing the service.

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.

flowchart LR A[Source code] --> B["Quarkus build\n(container-image extension)"] B --> C[Image pushed to registry] C --> D["kubernetes.yml / openshift.yml\ngenerated at build time"] D --> E["kubectl apply / oc apply\nor -Dquarkus.kubernetes.deploy=true"] E --> F["Running in cluster\n(Kubernetes or OpenShift)"] F --> G[Liveness / readiness probes]

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.