Keycloak, when deployed on Kubernetes, isn’t just another application; it’s a distributed, stateful system that demands careful consideration of its internal workings, especially around persistence and clustering.

Let’s see Keycloak in action, not just as a set of Pods, but as a live, functioning identity provider. Imagine a simple web application that needs to authenticate users.

# client-app-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-client-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-client-app
  template:
    metadata:
      labels:
        app: my-client-app
    spec:
      containers:
      - name: app
        image: quay.io/keycloak/keycloak-quickstart-demo-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: KEYCLOAK_URL
          value: "http://keycloak.default.svc.cluster.local:8080/auth/" # Assuming Keycloak is in the 'default' namespace
        - name: KEYCLOAK_REALM
          value: "myrealm"
        - name: KEYCLOAK_CLIENT_ID
          value: "myclient"

When a user tries to access my-client-app, the app container will redirect them to the Keycloak service (keycloak.default.svc.cluster.local:8080/auth/). Keycloak then handles the authentication flow. If successful, it issues a JWT, which the my-client-app verifies, granting access. This entire dance, from redirect to token validation, is orchestrated by Keycloak’s internal logic.

The problem Keycloak solves is centralizing authentication and authorization for multiple applications. Instead of each app rolling its own login mechanism, they delegate this to a single, robust system. Keycloak acts as an OAuth 2.0 and OpenID Connect provider, a standard way for applications to securely delegate authentication.

Internally, Keycloak is a Java application running on WildFly. It exposes its functionality via REST APIs, which clients interact with. For a distributed deployment on Kubernetes, the critical components are:

  1. Statelessness (mostly): The Keycloak server instances themselves should ideally be stateless. All persistent state (user data, realm configurations, client secrets) must be stored externally.
  2. Clustering: To achieve high availability and scalability, Keycloak nodes need to communicate with each other. This is typically done using JGroups, which relies on multicast or unicast for node discovery and message passing. On Kubernetes, multicast is often problematic, so unicast is the preferred method.
  3. Persistence: User credentials, realm definitions, client configurations, and sessions must be stored durably. This is usually done with a relational database (PostgreSQL, MySQL, etc.).

The Helm chart for Keycloak abstracts away much of this complexity. When you deploy it, you’ll configure several key parameters:

  • image.tag: The specific Keycloak version to deploy.
  • replicas: The number of Keycloak pods.
  • extraEnv: Environment variables for the Keycloak container. Crucially, JGROUPS_DISCOVERY_PROTOCOL=dns.dns_ping is often used in Kubernetes for cluster discovery, and JGROUPS_DISCOVERY_PROPERTIES=dns.query=keycloak-service.keycloak.svc.cluster.local (adjusting keycloak-service and namespace as needed) tells JGroups how to find other nodes.
  • postgresql.enabled / mysql.enabled: Whether to deploy a managed database or use an external one.
  • postgresql. Perempuan: For PostgreSQL, you’ll configure postgresql.auth.username, postgresql.auth.password, and postgresql.auth.database to set up the database Keycloak will use.
  • args: Command-line arguments passed to the Keycloak entrypoint. This is where you often set start-dev for development or start for production, and configure HTTP/HTTPS ports.

The most surprising truth about Keycloak on Kubernetes is how its clustering mechanism interacts with Kubernetes networking. While JGroups is designed for peer-to-peer discovery, in a Kubernetes environment, you don’t want nodes directly discovering each other via IP addresses that might change. Instead, you leverage Kubernetes DNS. When JGROUPS_DISCOVERY_PROTOCOL is set to dns.dns_ping, Keycloak pods query a specific Kubernetes Service (like keycloak-service.keycloak.svc.cluster.local) to find other healthy Keycloak pods. This Service acts as a stable rendezvous point, abstracting away the ephemeral nature of Pod IPs. The dns.query property then specifies the DNS name to resolve for discovering peers.

The next concept you’ll likely grapple with is managing Keycloak’s administrative interface and securing it, especially when using an external database or when you need to automate realm and client creation.

Want structured learning?

Take the full Keycloak course →