Install Keycloak with Helm: Chart Options vs Keycloak Operator

Tested on Red Hat Enterprise Linux 10.2 (Coughlan)
Package Helm v3.22.0
codecentric/keycloakx chart 7.3.1 (app 26.7.3)
quay.io/keycloak/keycloak:26.7.3
mirror.gcr.io/library/postgres:16
Applies to Any Kubernetes cluster with Helm 3 and permission to create Deployments or StatefulSets, Services, and Secrets
Privilege Namespace admin rights for the Helm release; cluster-admin when chart values request cluster-scoped RBAC
Lab environment Existing Kubernetes cluster — commands were verified on a single-node kind cluster (Kubernetes v1.32.2)
Scope Compare third-party Keycloak Helm charts with the official Keycloak Operator, install Keycloak 26.7.3 with the codecentric/keycloakx chart and external PostgreSQL, configure bootstrap admin credentials, upgrade and uninstall the release — not cluster creation, production Ingress/TLS, or realm import automation
Related guides Install Keycloak with Docker Compose
Short on time?Beta Get the gist of this 19-minute read in seconds, then ask follow-up questions

In this article we install Keycloak on Kubernetes with Helm — but first we answer a question many guides skip: Keycloak upstream does not publish an official Helm chart for the Keycloak server itself.

  • The supported Kubernetes path from the Keycloak project is the Keycloak Operator
  • Helm remains useful when you deliberately choose a maintained community chart and accept third-party packaging trade-offs

When you finish this walkthrough:

  • Keycloak 26.7.3 runs from a codecentric/keycloakx Helm release backed by PostgreSQL
  • You can sign in to the Admin Console through kubectl port-forward
  • You can decide when a community Helm chart fits your platform versus the Keycloak Operator path

If you only need the finished values YAML, jump to Create the Keycloak values file.

IMPORTANT
This article covers community Helm charts that render Keycloak Pods directly. It does not cover the official Keycloak Operator install path — use Install Keycloak on Kubernetes with the Keycloak Operator when you want upstream-supported lifecycle management on Kubernetes.

Does Keycloak have an official Helm chart?

No. As of Keycloak 26.7.x, upstream distributes Kubernetes support through the Keycloak Operator, not through a first-party Helm chart for the Keycloak server.

The official install paths are:

  • Operator Lifecycle Manager (OLM) on OpenShift or clusters that already run OLM
  • Kustomize manifests from the keycloak/keycloak-k8s-resources repository, for example kubectl apply -k 'github.com/keycloak/keycloak-k8s-resources/kubernetes?ref=26.7.3'

Third parties publish Helm charts that wrap Keycloak as a StatefulSet or Deployment. Some charts wrap the Operator instead of the server. Those charts are not maintained by the Keycloak core team, and their defaults, images, and upgrade behavior differ from the Operator path.

If your search query is "Keycloak Helm chart Kubernetes", you are usually looking for one of those community charts — or you may actually want the Operator guide linked above.


Keycloak Helm chart options

Because Keycloak does not publish its own server Helm chart, choosing a Helm chart also means choosing who maintains the packaging around Keycloak.

Helm chart quality changes over time. Check chart version, app version, default container image, and last update before you pin a release in production.

Chart Maintainer Default Keycloak image Database Example chart/app version when reviewed Update posture
codecentric/keycloakx codecentric AG (community) quay.io/keycloak/keycloak (official Quay image) External PostgreSQL via database.* values; dev H2 if unset 7.3.1 / 26.7.3 Active; chart appVersion tracks recent Keycloak releases
bitnami/keycloak Broadcom Bitnami Bitnami-packaged Keycloak image (historical images may appear under bitnamilegacy/*) Optional bundled PostgreSQL subchart 25.4.0 / 26.3.3 26.3.3 application version when reviewed; behind Keycloak 26.7.x. Verify current chart and image availability before adopting
adfinis/keycloak (and similar) Community / vendor Installs the Keycloak Operator, not a standalone server chart Via Operator CR Varies Different goal — Operator install via Helm, not direct Keycloak Pod chart

This guide deliberately installs codecentric/keycloakx because it:

  • Uses the official Quay Keycloak image
  • Ships current 26.7.x chart versions
  • Documents a plain PostgreSQL-backed start deployment

Treat bitnami/keycloak as a separate decision: compare Bitnami licensing, image lineage, and chart freshness against your Keycloak version target before you adopt it.

List current chart versions before you install. If Helm releases are new to you, Deploy Applications on Kubernetes with Helm explains repositories, helm install, upgrades, and rollbacks.

Helm repositories are indexes from which the Helm CLI discovers downloadable charts. Add codecentric's repository so Helm can locate keycloakx:

bash
helm repo add codecentric https://codecentric.github.io/helm-charts

Refresh the local chart index:

bash
helm repo update

Search for the pinned chart line:

bash
helm search repo codecentric/keycloakx --versions | head -n 5

Sample output:

output
NAME                 	CHART VERSION	APP VERSION	DESCRIPTION
codecentric/keycloakx	7.3.1        	26.7.3     	Keycloak.X - Open Source Identity and Access Ma...
codecentric/keycloakx	7.3.0        	26.7.2     	Keycloak.X - Open Source Identity and Access Ma...
codecentric/keycloakx	7.2.3        	26.6.4     	Keycloak.X - Open Source Identity and Access Ma...
codecentric/keycloakx	7.2.2        	26.6.4     	Keycloak.X - Open Source Identity and Access Ma...
codecentric/keycloakx	7.2.0        	26.6.2     	Keycloak.X - Open Source Identity and Access Ma...

The chart version identifies the Helm packaging, while APP VERSION identifies the Keycloak version that chart release is designed to deploy.

Choose a chart release whose app version matches the Keycloak version you intend to run.


Helm vs Keycloak Operator

Both paths run Keycloak on Kubernetes, but they optimize for different operators.

Topic Helm (codecentric/keycloakx) Keycloak Operator (official)
Deployment lifecycle Helm release owns StatefulSet, Services, Secrets Operator reconciles Keycloak custom resources
Upgrades helm upgrade with chart + values diff Change CR spec or Operator version; Operator rolls Pods
Configuration Chart values, extraEnv, command args CR fields (spec.http, spec.hostname, database secret refs, …)
Database You provision PostgreSQL (or rely on dev defaults) You provision PostgreSQL; CR references Secrets
Realm imports Manual job or chart-specific automation Operator provides a documented KeycloakRealmImport workflow
Upstream support Community chart; verify issues on chart repo Keycloak project's documented Kubernetes integration
Complexity Familiar to teams already standardized on Helm Extra CRD + Operator install, closer to upstream docs
  • Use the Operator when you want Keycloak's first-party Kubernetes lifecycle tooling and documentation
  • Use a community Helm chart when your platform deliberately standardizes on Helm and you accept the chart's separate maintenance lifecycle

What this Helm lab creates

At the end of this lab you will have:

  • Namespace keycloak-helm with a PostgreSQL StatefulSet and Service keycloak-db-postgresql
  • Helm release keycloak from chart codecentric/keycloakx 7.3.1
  • Keycloak Pod keycloak-keycloakx-0 using quay.io/keycloak/keycloak:26.7.3
  • ClusterIP Service keycloak-keycloakx-http for HTTP on port 80 inside the cluster
  • Bootstrap admin user admin with password supplied through the chart's secrets values block
  ┌─────────────┐   JDBC :5432    ┌──────────────────────────┐
  │ Helm release│ ──────────────► │ PostgreSQL StatefulSet   │
  │ keycloak    │                 │ keycloak-db-postgresql   │
  └──────┬──────┘                 │ (emptyDir — lab only)    │
         │                        └──────────────────────────┘
         │ StatefulSet keycloak-keycloakx
         ▼
  ┌──────────────────────────┐      kubectl port-forward
  │ quay.io/keycloak/keycloak│ ───► http://127.0.0.1:8080/auth/
  └──────────────────────────┘

Keycloak stores its realm data in PostgreSQL rather than its embedded development database.

  • The lab-tested PostgreSQL instance uses emptyDir, so its data survives a container restart within the same Pod but is lost when the PostgreSQL Pod is replaced
  • Reinstalling only the Keycloak Helm release does not delete PostgreSQL
  • Replacing the lab PostgreSQL Pod loses its database

Requirements

You need a working Kubernetes cluster and Helm 3 before any chart install.

Confirm the API server answers:

bash
kubectl get nodes

Sample output:

output
NAME                         STATUS   ROLES           AGE   VERSION
keycloak-lab-control-plane   Ready    control-plane   45m   v1.32.2

Ready in the STATUS column means the cluster API is reachable.

Confirm Helm 3 is available:

bash
helm version --short

Sample output:

output
v3.22.0+g144ca65

The v3.x prefix confirms Helm 3, which this chart requires.

This guide pins codecentric/keycloakx chart version 7.3.1, whose appVersion is Keycloak 26.7.3.

Before using another chart version, run helm search repo codecentric/keycloakx --versions and verify the corresponding Keycloak application version. Allow about 4 GiB of free memory on small single-node labs while PostgreSQL and Keycloak start.


Choose how to configure Keycloak with Helm

A Helm chart is a collection of Kubernetes manifest templates. A values.yaml file supplies inputs to those templates.

Helm combines the chart templates with your values and generates Kubernetes resources such as StatefulSets, Services, and Secrets. The codecentric/keycloakx chart does not pass values.yaml itself to Keycloak. Instead, it converts relevant values into:

  • Container arguments
  • Environment variables
  • Volumes
  • Other Kubernetes configuration

Keycloak reads server options from environment variables with the KC_ prefix — for example, health-enabled becomes KC_HEALTH_ENABLED.

Method Example Best suited for
Chart-specific values database.vendor: postgres Settings the chart maps to KC_* variables for you
command start, --http-port=8080, --hostname-strict=false Startup mode and CLI flags; outrank matching KC_* variables
extraEnv KC_BOOTSTRAP_ADMIN_* from a Secret Additional KC_* variables when the chart has no dedicated value
Mounted keycloak.conf Chart extraVolumes + extraVolumeMounts Advanced file-based configuration; see chart values and the Keycloak configuration guide

You can combine methods — for example, database.* for JDBC settings and extraEnv for bootstrap credentials from a Kubernetes Secret.

The chart's command: value becomes the command executed inside the Keycloak container. In this lab it runs kc.sh start and passes additional Keycloak CLI options — not start-dev; see Keycloak start vs start-dev for why this walkthrough uses production start with external PostgreSQL.

  • extraEnv lets you add environment variables to the Keycloak container when the chart does not expose a dedicated value for what you need
  • extraEnv is a template-rendered string in this chart, which is why Helm expressions such as {{ include "keycloak.fullname" . }} work inside it

This guide uses chart-specific values (database.*, command) plus extraEnv and the chart's secrets block for bootstrap admin credentials. The chart also injects defaults you did not set — for example KC_HTTP_RELATIVE_PATH=/auth, which is why the Admin Console URL includes /auth/ later.

For build-time vs runtime options, precedence, mounted keycloak.conf details, and troubleshooting, see Keycloak keycloak.conf configuration.


Configure PostgreSQL

Keycloak running with the production profile expects a real database. The chart's database section maps to Keycloak's --db-* settings.

Create the lab namespace:

bash
kubectl create namespace keycloak-helm

If the namespace already exists, Kubernetes reports AlreadyExists; continue using the existing namespace if it belongs to this lab.

Lab-tested path — minimal ephemeral PostgreSQL StatefulSet

This walkthrough uses a single PostgreSQL Pod backed by emptyDir. That ephemeral volume type is covered in Kubernetes volumes.

  • Data survives a container restart inside that Pod, but not replacement of the PostgreSQL Pod
  • Use this only for a disposable lab
  • For persistent Kubernetes storage, use a PersistentVolume and PVC or an externally managed PostgreSQL database

The manifest uses a StatefulSet because PostgreSQL is a stateful workload and the stable Pod name makes the lab predictable, but emptyDir deliberately keeps this example disposable. A production StatefulSet would normally use a volumeClaimTemplate or external database storage.

POSTGRES_USER, POSTGRES_PASSWORD, and POSTGRES_DB initialize PostgreSQL only when the data directory is empty. Changing them later does not automatically change an existing role or database name.

The manifest uses mirror.gcr.io/library/postgres:16 because the test cluster hit Docker Hub pull-rate limits. postgres:16 is the normal official image reference; use your organization's registry or mirror policy in real environments.

Save this manifest as keycloak-db-postgresql.yaml:

yaml
apiVersion: v1
kind: Service
metadata:
  name: keycloak-db-postgresql
  namespace: keycloak-helm
spec:
  ports:
  - port: 5432
    targetPort: 5432
  selector:
    app: keycloak-db-postgresql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: keycloak-db-postgresql
  namespace: keycloak-helm
spec:
  serviceName: keycloak-db-postgresql
  replicas: 1
  selector:
    matchLabels:
      app: keycloak-db-postgresql
  template:
    metadata:
      labels:
        app: keycloak-db-postgresql
    spec:
      containers:
      - name: postgresql
        image: mirror.gcr.io/library/postgres:16
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_USER
          value: dbusername
        - name: POSTGRES_PASSWORD
          value: dbpassword
        - name: POSTGRES_DB
          value: keycloak
        - name: PGDATA
          value: /var/lib/postgresql/data/pgdata
        readinessProbe:
          exec:
            command: ["sh", "-c", "pg_isready -U dbusername -d keycloak"]
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: pgdata
          mountPath: /var/lib/postgresql/data
      volumes:
      - name: pgdata
        emptyDir: {}

The Service selector matches the app: keycloak-db-postgresql label on the PostgreSQL Pod. That label-to-selector wiring is how Kubernetes Services route traffic; here, connections to the Service on port 5432 reach that Pod.

Apply the manifest:

bash
kubectl apply -f keycloak-db-postgresql.yaml

Sample output:

output
service/keycloak-db-postgresql created
statefulset.apps/keycloak-db-postgresql created

Wait until the StatefulSet reports ready:

bash
kubectl -n keycloak-helm rollout status statefulset/keycloak-db-postgresql --timeout=120s

Sample output:

output
Waiting for 1 pods to be ready...
partitioned roll out complete: 1 new pods have been updated...

The StatefulSet rollout completes after the PostgreSQL Pod becomes Ready. In this manifest:

  • The readiness probe runs pg_isready
  • Ready means PostgreSQL is accepting database connections with the configured database and user check
Alternative: codecentric's upstream example uses the Bitnami PostgreSQL chart. If you prefer that path, add Bitnami's repository with helm repo add bitnami https://charts.bitnami.com/bitnami, then install a separate release such as helm install keycloak-db bitnami/postgresql --namespace keycloak-helm --values keycloak-db-values.yaml and follow the chart documentation for credentials and Service naming. That path was not used in the lab output shown here.

Create the Keycloak values file

For Keycloak 26.7.x, reference bootstrap administrator credentials from a Kubernetes Secret through extraEnv. The database.* keys must match the PostgreSQL user, password, and database name you provisioned above.

The chart can run a small dbchecker init container before Keycloak starts. Its job is to wait until PostgreSQL accepts connections.

This lab disables it because:

  • We already wait for PostgreSQL to become Ready before installing Keycloak
  • The test environment encountered Docker Hub limits while pulling its BusyBox image

Point the Keycloak release at the PostgreSQL Service with this complete keycloak-server-values.yaml:

yaml
command:
  - "/opt/keycloak/bin/kc.sh"
  - "--verbose"
  - "start"
  - "--http-port=8080"
  - "--hostname-strict=false"

dbchecker:
  enabled: false

database:
  vendor: postgres
  hostname: keycloak-db-postgresql.keycloak-helm.svc.cluster.local
  port: 5432
  username: dbusername
  password: dbpassword
  database: keycloak

extraEnv: |
  - name: KC_BOOTSTRAP_ADMIN_USERNAME
    valueFrom:
      secretKeyRef:
        name: {{ include "keycloak.fullname" . }}-admin-creds
        key: user
  - name: KC_BOOTSTRAP_ADMIN_PASSWORD
    valueFrom:
      secretKeyRef:
        name: {{ include "keycloak.fullname" . }}-admin-creds
        key: password

secrets:
  admin-creds:
    stringData:
      user: admin
      password: ChangeMe!2026
values.yaml
    ↓
chart creates Kubernetes Secret
    ↓
extraEnv references keys in Secret
    ↓
Kubernetes injects KC_BOOTSTRAP_ADMIN_* into Keycloak Pod
    ↓
Keycloak creates temporary bootstrap administrator

The secrets.admin-creds block asks the chart to create a Kubernetes Secret. extraEnv references the user and password keys from that Secret and exposes them to Keycloak as KC_BOOTSTRAP_ADMIN_USERNAME and KC_BOOTSTRAP_ADMIN_PASSWORD.

For this lab:

  • The chart creates a Kubernetes Secret from credentials stored in values.yaml, and extraEnv references that Secret
  • This keeps the credentials out of the rendered Pod environment definition
  • The password is still present in your Helm values and Helm release data

For production, use an externally managed Kubernetes Secret or another secrets-management workflow.

Kubernetes gives Services DNS names. In database.hostname:

  • keycloak-db-postgresql — Service name
  • keycloak-helm — namespace
  • svc.cluster.local — cluster Service DNS suffix

Because Keycloak and PostgreSQL are in the same namespace, the shorter hostname keycloak-db-postgresql would also work in a standard Kubernetes DNS setup; the full name makes the target explicit.

Save that file as keycloak-server-values.yaml and pass it to helm install or helm upgrade.

The chart's command: value runs kc.sh start and passes the CLI options shown above.

  • --hostname-strict=false allows this local port-forward lab to run without configuring a fixed production hostname
  • Do not treat that flag as production hostname configuration

After Keycloak creates the master realm, bootstrap variables are ignored for day-to-day admin login. For a longer-lived setup, use the temporary account to establish normal administrative access, then follow the Keycloak bootstrap admin guide for the appropriate cleanup lifecycle.

database.vendor: postgres selects PostgreSQL as Keycloak's database vendor. The stock image used here handles this setting during startup; optimized production images normally set the database vendor while building the image.

For deeper database tuning and backup expectations, see Configure Keycloak with PostgreSQL.


Install the Keycloak Helm chart

helm install renders the chart with your values and submits the generated Kubernetes resources to the keycloak-helm namespace.

The release name keycloak is how Helm tracks this installation for later upgrades, history, rollback, and uninstall.

In this command:

  • Release — keycloak
  • Chart — codecentric/keycloakx
  • Chart version — 7.3.1
  • Namespace — keycloak-helm
  • Values — keycloak-server-values.yaml

Install Keycloak from the pinned chart version and the values file you just created:

bash
helm install keycloak codecentric/keycloakx \
  --namespace keycloak-helm \
  --version 7.3.1 \
  --values keycloak-server-values.yaml

Sample output:

output
NAME: keycloak
LAST DEPLOYED: Mon Sep 14 22:53:18 2026
NAMESPACE: keycloak-helm
STATUS: deployed
REVISION: 1

STATUS: deployed means Helm successfully created or updated the release; it does not prove the Keycloak Pod is Ready. The next kubectl wait checks workload readiness.

Helm creates a StatefulSet named {release}-{chart} — here keycloak-keycloakx — plus ClusterIP Services for HTTP and headless discovery.

Wait until the Keycloak Pod reports Ready:

bash
kubectl -n keycloak-helm wait --for=condition=Ready pod/keycloak-keycloakx-0 --timeout=180s

Sample output:

output
pod/keycloak-keycloakx-0 condition met

First bootstrap on PostgreSQL commonly takes 60–90 seconds after the Pod starts. If kubectl wait times out, use kubectl logs, events, and describe to inspect the Pod before you change values.

Inspect Services and Pods:

bash
kubectl -n keycloak-helm get svc,pods

Sample output:

output
NAME                                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
service/keycloak-db-postgresql        ClusterIP   10.96.254.157   <none>        5432/TCP
service/keycloak-keycloakx-headless   ClusterIP   None            <none>        80/TCP
service/keycloak-keycloakx-http       ClusterIP   10.96.131.165   <none>        9000/TCP,80/TCP,8443/TCP

NAME                           READY   STATUS    RESTARTS   AGE
pod/keycloak-db-postgresql-0   1/1     Running   0          6m
pod/keycloak-keycloakx-0       1/1     Running   0          3m

1/1 means the Pod has one container and Kubernetes currently considers that container Ready.

  • Running alone is not enough; the Ready column shows Kubernetes considers the container ready to receive traffic
  • For PostgreSQL, that includes the pg_isready readiness probe defined above

When both Pods show 1/1 Ready, PostgreSQL and Keycloak are up.

PostgreSQL shows 5432/TCP without a host mapping because this lab does not publish the database outside the cluster. Keycloak reaches PostgreSQL on port 5432 through the cluster Service DNS name in database.hostname.

The HTTP Service name {release}-keycloakx-http is what you port-forward below.


Access Keycloak

The chart exposes HTTP on Service port 80 with default relative path /auth. Reach it locally with kubectl port-forward:

bash
kubectl -n keycloak-helm port-forward svc/keycloak-keycloakx-http 8080:80

Port-forward prints no output when it binds; leave that terminal open. From another shell, check the HTTP response with curl:

bash
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/auth/

Sample output:

output
302

HTTP 302 is a redirect response. Here it is expected: /auth/ redirects your browser toward Keycloak's login flow, so the response confirms that Keycloak is answering on the chart default path.

Open the Admin Console in a browser:

text
http://127.0.0.1:8080/auth/admin/

Sign in with the bootstrap user from your values file (admin / ChangeMe!2026 here).

Keycloak Admin Console login page after Helm install

The login form confirms that the Admin Console is reachable.


Change Keycloak configuration after installation

After you change keycloak-server-values.yaml, apply the new settings with:

bash
helm upgrade keycloak codecentric/keycloakx \
  --namespace keycloak-helm \
  --version 7.3.1 \
  --values keycloak-server-values.yaml

Sample output:

output
Release "keycloak" has been upgraded. Happy Helming!

Helm stores each successful upgrade as a new revision.

  • A Helm revision represents a stored release state created by an install, upgrade, or rollback
  • Every successful install or upgrade increments the revision, which is why Helm can roll the release back to an earlier state — for example, helm rollback keycloak 2 -n keycloak-helm

If the rendered StatefulSet Pod template changes, Kubernetes performs a StatefulSet rollout so the Keycloak Pod is recreated with the new configuration. A values change that does not alter the rendered Pod template will not necessarily restart the Pod.

Keep each setting in one delivery method:

  • Arguments in command outrank KC_* variables from extraEnv or chart defaults
  • Do not set --http-port=8080 in command and also add KC_HTTP_PORT to extraEnv

Changing database.* credentials in values alone does not change passwords inside an existing PostgreSQL database. Update the role in PostgreSQL and match Keycloak's database.* settings, or rebuild the database on a disposable lab.

For resolved values and precedence rules, use the show-config workflow in the Keycloak configuration guide.

Review release history before and after changes:

bash
helm history keycloak -n keycloak-helm

Sample output:

output
REVISION	UPDATED                 	STATUS    	CHART          	APP VERSION	DESCRIPTION
1       	Mon Sep 14 22:53:18 2026	superseded	keycloakx-7.3.1	26.7.3     	Install complete
2       	Mon Sep 14 22:55:33 2026	superseded	keycloakx-7.3.1	26.7.3     	Upgrade complete
3       	Mon Sep 14 22:59:06 2026	deployed  	keycloakx-7.3.1	26.7.3     	Upgrade complete

The latest row with deployed status is the active revision. Roll back with helm rollback keycloak <revision> -n keycloak-helm when an upgrade misbehaves.

Pin a new chart or app version with --version, read the Keycloak release notes before you upgrade production data, and watch Pod readiness the same way as the initial install.


Uninstall the Keycloak Helm release

Stop any running kubectl port-forward session first.

Uninstall the Keycloak Helm release:

bash
helm uninstall keycloak -n keycloak-helm

Remove PostgreSQL depending on how you installed it:

  • Lab-tested manual StatefulSet:
bash
kubectl delete -f keycloak-db-postgresql.yaml
  • Bitnami PostgreSQL chart:
bash
helm uninstall keycloak-db -n keycloak-helm

Delete the namespace when the lab is finished:

bash
kubectl delete namespace keycloak-helm --wait=true --timeout=120s

Deleting the namespace removes any remaining namespaced Secrets and other lab resources. Do not delete the namespace if it contains unrelated workloads.


Common Helm problems

Symptom Likely cause Fix
Init container dbchecker stuck in ImagePullBackOff Docker Hub rate limit on busybox Confirm PostgreSQL is ready; set dbchecker.enabled: false or mirror the init image
Keycloak Pod CrashLoopBackOff after install or upgrade Keycloak startup failure: invalid option, database connection or migration error, incompatible image or config kubectl logs -n keycloak-helm keycloak-keycloakx-0 --previous; inspect the actual Keycloak error before changing values
Admin login fails despite successful install Wrong bootstrap Secret or realm already initialized with different password Check {release}-keycloakx-admin-creds; recreate only on a fresh database
helm upgrade shows no Pod change Values typo or change did not alter the Pod template Run helm get values keycloak -n keycloak-helm to confirm stored values; use helm get manifest to inspect what the installed release rendered, or helm template to preview what a candidate values file would render; confirm your change reaches the StatefulSet Pod spec
Browser shows wrong host or redirect loop hostname-strict or Ingress rules disagree with port-forward URL For local labs keep --hostname-strict=false; configure ingress.* for real hostnames
PostgreSQL connection errors Service name or credentials mismatch Match database.hostname to PostgreSQL Service DNS; verify username, password, and database name agree on both sides
rendered manifests contain a resource that already exists Leftover Secret or Service from a prior install or manual kubectl create helm uninstall the old release, delete the conflicting object, or follow rendered manifests already exist for shared resources
Data missing after PostgreSQL Pod replacement Lab PostgreSQL uses emptyDir Expected on this lab path; use a PVC or external PostgreSQL for durable storage

Summary

Keycloak upstream ships the Operator for Kubernetes, not an official Keycloak server Helm chart. Community charts such as codecentric/keycloakx and bitnami/keycloak fill that gap with different images, freshness, and support expectations — and Operator-wrapper charts are a third category entirely.

In this lab we:

  • Installed Keycloak 26.7.3 with codecentric/keycloakx 7.3.1 and ephemeral lab PostgreSQL
  • Stored bootstrap admin credentials in chart values and injected them through a chart-created Kubernetes Secret
  • Verified HTTP through kubectl port-forward on the /auth/ path
  • Used chart-specific values, command, and extraEnv to deliver configuration to the Pod

Change settings with helm upgrade and roll back through Helm revision history when needed.

To tear down the lab:

  • Run helm uninstall keycloak
  • Delete the PostgreSQL manifest, or helm uninstall keycloak-db when you used the Bitnami chart
  • Delete the namespace

Helm does not remove PostgreSQL you installed outside the Keycloak release.

  • Use the Operator when you want Keycloak's first-party Kubernetes lifecycle tooling
  • Use Helm when your platform standardizes on chart releases and you accept the community chart's separate maintenance lifecycle

For the Operator walkthrough on the same Keycloak version, use the Operator guide linked in the scope alert at the top of this page.


References


Frequently Asked Questions

Does Keycloak publish an official Helm chart for the server?

No. Upstream documents the Keycloak Operator for Kubernetes. Helm charts that install Keycloak Pods come from third-party maintainers such as codecentric or Bitnami.

Which Keycloak Helm chart does this guide install?

The codecentric/keycloakx chart pinned at version 7.3.1, deploying application version 26.7.3 with the official quay.io/keycloak/keycloak image.

Why does the Admin Console URL include /auth/?

The chart sets http.relativePath to /auth by default for compatibility with older Keycloak paths. Port-forward to the HTTP Service and open /auth/admin/.

Can I use the Bitnami Keycloak Helm chart instead?

You can, but verify before you adopt it:

  • Chart app version and container image source
  • Bitnami licensing and support model
  • Bitnami changed its free image and catalog publishing model in 2025; historical images may be available through bitnamilegacy

Does helm uninstall delete PostgreSQL?

No. PostgreSQL installed separately — Bitnami release or manual StatefulSet — remains until you delete that release or manifest.

Finished reading?Beta Get the key takeaways in seconds, then ask follow-up questions
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with 15+ years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)