
There are multiple ways to authenticate in OpenShift.
- htpasswd OAuth Identity Provider
- kubeadmin
- Keycloak
- Red Hat build of Keycloak Operator (this article)
- Getting Started with Keycloak
- SSO
Here are my notes as I prepared for the Red Hat Certified Specialist in OpenShift Automation and Integration exam (EX380).
In the OpenShift console, at Operators > Operator Hub search for the Red Hat build of Keycloak Operator.

And follow the prompts to install the operator.

The Operator should create a deployment which should spawn a replica set which should spawn a pod.
~]$ oc get all --namespace keycloak
NAME READY STATUS RESTARTS AGE
pod/rhbk-operator-57b47d9d66-56q58 1/1 Running 0 13s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/rhbk-operator 1/1 1 1 23h
NAME DESIRED CURRENT READY AGE
replicaset.apps/rhbk-operator-57b47d9d66 1 1 1 13s
If the pod is in an Error state.
~]$ oc get pods --namespace keycloak
NAME READY STATUS RESTARTS AGE
pod/rhbk-operator-65ffd96644-f4wvx 0/1 Error 0 6s
And the pod logs has something like this.
~]$ oc logs pod/rhbk-operator-65ffd96644-f4wvx
--namespace keycloak
2025-10-02 01:13:02,481 ERROR [io.qua.run.Application] (main) Failed to start application: java.lang.RuntimeException: Failed to start quarkus
at io.quarkus.runner.ApplicationImpl.doStart(Unknown Source)
at io.quarkus.runtime.Application.start(Application.java:101)
at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:121)
at io.quarkus.runtime.Quarkus.run(Quarkus.java:77)
at io.quarkus.runtime.Quarkus.run(Quarkus.java:48)
at io.quarkus.runtime.Quarkus.run(Quarkus.java:137)
at io.quarkus.runner.GeneratedMain.main(Unknown Source)
at io.quarkus.bootstrap.runner.QuarkusEntryPoint.doRun(QuarkusEntryPoint.java:68)
at io.quarkus.bootstrap.runner.QuarkusEntryPoint.main(QuarkusEntryPoint.java:36)
Caused by: java.lang.IllegalArgumentException: Failure in creating proxy URL. Proxy port is required!
This may occur if your OpenShift cluster has a proxy configured.
- FreeKB - OpenShift - HTTP_PROXY HTTPS_PROXY and NO_PROXY
- FreeKB - OpenShift - List proxy servers using the oc get proxy command
In this scenario, you may need to adjust the proxy configurations in the keycloak deployment. For example, I resolved the pod being in an Error state by removing the following from my keycloak deployment.
- name: HTTP_PROXY
value: http://proxy.example.com
- name: HTTPS_PROXY
value: https://proxy.example.com
- name: NO_PROXY
value: .cluster.local,.access.redhat.com,cloud.openshift.com,localhost,quay.io,registry.connect.redhat.com,registry.redhat.io
A stable pod should have logs that look something like this. Notice the pod is listening for connections on http://0.0.0.0:8080.
~]$ oc logs pod/rhbk-operator-57b47d9d66-56q58 --namespace keycloak
2025-10-02 01:14:26,319 WARN [io.qua.config] (main) Unrecognized configuration key "quarkus.operator-sdk.bundle.package-name" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
2025-10-02 01:14:27,500 INFO [io.qua.ope.run.OperatorProducer] (main) Quarkus Java Operator SDK extension 7.1.1.redhat-00002 (commit: bbd08ea) built on Mon Apr 07 13:41:33 GMT 2025
2025-10-02 01:14:27,516 INFO [io.jav.ope.Operator] (main) Registered reconciler: 'keycloakrealmimportcontroller' for resource: 'class org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImport' for namespace(s): [keycloak]
2025-10-02 01:14:27,600 INFO [io.jav.ope.Operator] (main) Registered reconciler: 'keycloakcontroller' for resource: 'class org.keycloak.operator.crds.v2alpha1.deployment.Keycloak' for namespace(s): [keycloak]
2025-10-02 01:14:27,600 INFO [io.qua.ope.run.AppEventListener] (main) Starting operator.
2025-10-02 01:14:27,600 INFO [io.jav.ope.Operator] (main) Operator SDK 5.0.4.redhat-00001 (commit: 2a58f2b) built on Thu Apr 03 15:50:23 GMT 2025 starting...
2025-10-02 01:14:27,600 INFO [io.jav.ope.Operator] (main) Client version: 7.1.0.redhat-00001
2025-10-02 01:14:27,601 INFO [io.jav.ope.pro.Controller] (Controller Starter for: keycloakrealmimportcontroller) Starting 'keycloakrealmimportcontroller' controller for reconciler: org.keycloak.operator.controllers.KeycloakRealmImportController, resource: org.keycloak.operator.crds.v2alpha1.realmimport.KeycloakRealmImport
2025-10-02 01:14:27,601 INFO [io.jav.ope.pro.Controller] (Controller Starter for: keycloakcontroller) Starting 'keycloakcontroller' controller for reconciler: org.keycloak.operator.controllers.KeycloakController, resource: org.keycloak.operator.crds.v2alpha1.deployment.Keycloak
2025-10-02 01:14:27,800 WARN [io.fab.kub.cli.dsl.int.VersionUsageUtils] (InformerWrapper [keycloakrealmimports.k8s.keycloak.org/v2alpha1] 27) The client is using resource type 'keycloakrealmimports' with unstable version 'v2alpha1'
2025-10-02 01:14:27,800 WARN [io.fab.kub.cli.dsl.int.VersionUsageUtils] (InformerWrapper [keycloaks.k8s.keycloak.org/v2alpha1] 26) The client is using resource type 'keycloaks' with unstable version 'v2alpha1'
2025-10-02 01:14:29,394 INFO [io.jav.ope.pro.Controller] (Controller Starter for: keycloakrealmimportcontroller) 'keycloakrealmimportcontroller' controller started
2025-10-02 01:14:29,697 INFO [io.jav.ope.pro.Controller] (Controller Starter for: keycloakcontroller) 'keycloakcontroller' controller started
2025-10-02 01:14:29,716 INFO [io.quarkus] (main) keycloak-operator 26.2.9.redhat-00001 on JVM (powered by Quarkus 3.20.2.redhat-00004) started in 4.293s. Listening on: http://0.0.0.0:8080
2025-10-02 01:14:29,716 INFO [io.quarkus] (main) Profile prod activated.
2025-10-02 01:14:29,716 INFO [io.quarkus] (main) Installed features: [cdi, kubernetes, kubernetes-client, openshift-client, operator-sdk, smallrye-context-propagation, smallrye-health, vertx]
Also, the deployment should have label name: rhbk-operator.
~]$ oc get pod/rhbk-operator-57b47d9d66-56q58 --namespace keycloak --output jsonpath="{.metadata.labels}"
{"name":"rhbk-operator","pod-template-hash":"57b47d9d66"}
Keycloak Service
Next let's create a file named keycloak_service.yml that contains the following YAML. In this example since the pod has label name: rhbk-operator the service then has selector name: rhbk-operator.
apiVersion: v1
kind: Service
metadata:
name: keycloak
namespace: keycloak
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
name: rhbk-operator
sessionAffinity: None
type: ClusterIP
And then the oc apply command can be used to create the service.
oc apply --filename service.yaml
And the oc describe command should show that the service Endpoint is pointing to the IP address and port of the keycloak pod.
~]$ oc describe service --namespace keycloak
Name: keycloak
Namespace: keycloak
Labels: <none>
Annotations: <none>
Selector: app=keycloak
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 172.30.245.108
IPs: 172.30.245.108
Port: <unset> 8080/TCP
TargetPort: 8080/TCP
Endpoints: 10.128.3.72:8080
Session Affinity: None
Events: <none>
Keycloak Route
Next let's create a file named keycloak_route.yml that contains the following YAML, replacing "keycloak.apps.openshift.example.com" with the URL you want to use for your keycloak route.
apiVersion: route.openshift.io/v1
kind: Route
metadata:
labels:
app: keycloak
route-type: internal
name: keycloak
namespace: keycloak
spec:
host: keycloak.apps.openshift.example.com
tls:
termination: edge
to:
kind: Service
name: keycloak
weight: 100
wildcardPolicy: None
And then the oc apply command can be used to create the route.
oc apply --filename route.yaml
And the oc describe command should show that the route is exposed on one of the OpenShift routes and is forwarding onto the keycloak service, which is turns forwards onto the keycloak pod. Notice in this example that the route HOST/PORT is keycloak-keycloak.apps.openshift.example.com.
~]$ oc describe route keycloak --namespace keycloak
Name: keycloak
Namespace: keycloak
Created: 7 days ago
Labels: app=keycloak
route-type=default
Description: Route for application's service.
Annotations: openshift.io/host.generated=true
Requested Host: keycloak.apps.openshift.example.com
exposed on router default-router (host router-default-router.apps.openshift.example.com) 1 minute ago
Path: <none>
TLS Termination: edge
Insecure Policy: <none>
Endpoint Port: <all endpoint ports>
Service: keycloak
Weight: 100 (100%)
Endpoints: <none>
Now you can go to the URL of your route and if all goes according to plan, you should be presented with the admin console sign in page. Awesome!

You should be able to sign into the console using the KC_BOOTSTRAP_ADMIN_USERNAME and KC_BOOTSTRAP_ADMIN_PASSWORD, which is admin and itsasecret in this example.
~]$ oc exec pod/keycloak-c857bfb4-4jkxr --namespace keycloak -- printenv
KC_BOOTSTRAP_ADMIN_USERNAME=admin
KC_BOOTSTRAP_ADMIN_PASSWORD=itsasecret
Once signed into the console, you should see something like this.

Did you find this article helpful?
If so, consider buying me a coffee over at