
The oc new-project command can be used to create an isolated project/namespace for keycloak.
oc new-project keycloak
Let's create a YAML file called keycloak_deployment_config.yaml that contains the following YAML. To be able to access the Keycloak admin console (more on that in a moment), I had to include the KC_PROXY_HEADERS xfowarded variable. It's also import to recongize that in this example, I'm using the quay.io/keycloak/keycloak:26.0.0 image, meaning I'm deploying Keycloak version 26. This YAML may be slightly different for different versions of Keycloak.
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
labels:
application: keycloak
name: keycloak
namespace: keycloak
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
deploymentConfig: keycloak
strategy:
activeDeadlineSeconds: 21600
recreateParams:
timeoutSeconds: 600
resources: {}
type: Recreate
template:
metadata:
creationTimestamp: null
labels:
application: keycloak
deploymentConfig: keycloak
name: keycloak
spec:
containers:
- args:
- start-dev
env:
- name: KC_BOOTSTRAP_ADMIN_USERNAME
value: admin
- name: KC_BOOTSTRAP_ADMIN_PASSWORD
value: itsasecret
- name: KC_PROXY_HEADERS
value: xforwarded
image: quay.io/keycloak/keycloak:26.0.0
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 100
httpGet:
path: /
port: 8080
scheme: HTTP
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: keycloak
ports:
- containerPort: 8080
protocol: TCP
readinessProbe:
failureThreshold: 300
httpGet:
path: /
port: 8080
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
resources: {}
securityContext:
privileged: false
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /opt/keycloak/data
name: empty
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- emptyDir: {}
name: empty
test: false
triggers:
- type: ConfigChange
And then the oc apply command can be used to create the deployment config, replication controller, and pods.
oc apply --filename keycloak_deployment_config.yaml
And then the oc get all command can be used to see the deployment config, service, pod, replication controller, and route. Notice in this example that the route HOST/PORT is keycloak-keycloak.apps.openshift.example.com.
~]$ oc get all
NAME READY STATUS RESTARTS AGE
pod/keycloak-1-deploy 0/1 Completed 0 56s
pod/keycloak-1-vjx6s 1/1 Running 0 54s
NAME DESIRED CURRENT READY AGE
replicationcontroller/keycloak-1 1 1 1 56s
NAME REVISION DESIRED CURRENT TRIGGERED BY
deploymentconfig.apps.openshift.io/keycloak 1 1 1 config
The pod logs should return something like this.
~]$ oc logs pod/keycloak-3-ptjr6
2024-10-23 01:50:09,998 INFO [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 3325ms
2024-10-23 01:50:11,878 INFO [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) Starting Infinispan embedded cache manager
2024-10-23 01:50:11,952 INFO [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) Persistent user sessions enabled and no memory limit found in configuration. Setting max entries for sessions to 10000 entries.
2024-10-23 01:50:11,952 INFO [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) Persistent user sessions enabled and no memory limit found in configuration. Setting max entries for clientSessions to 10000 entries.
2024-10-23 01:50:11,952 INFO [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) Persistent user sessions enabled and no memory limit found in configuration. Setting max entries for offlineSessions to 10000 entries.
2024-10-23 01:50:11,953 INFO [org.keycloak.quarkus.runtime.storage.infinispan.CacheManagerFactory] (main) Persistent user sessions enabled and no memory limit found in configuration. Setting max entries for offlineClientSessions to 10000 entries.
2024-10-23 01:50:12,158 INFO [org.infinispan.CONTAINER] (ForkJoinPool.commonPool-worker-1) ISPN000556: Starting user marshaller 'org.infinispan.commons.marshall.ImmutableProtoStreamMarshaller'
2024-10-23 01:50:12,584 INFO [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: node_355364, Site name: null
2024-10-23 01:50:13,040 INFO [org.keycloak.quarkus.runtime.storage.database.liquibase.QuarkusJpaUpdaterProvider] (main) Initializing database schema. Using changelog META-INF/jpa-changelog-master.xml
UPDATE SUMMARY
Run: 144
Previously run: 0
Filtered out: 0
-------------------------------
Total change sets: 144
2024-10-23 01:50:14,395 WARN [io.agroal.pool] (main) Datasource '<default>': JDBC resources leaked: 1 ResultSet(s) and 0 Statement(s)
2024-10-23 01:50:14,673 INFO [org.keycloak.broker.provider.AbstractIdentityProviderMapper] (main) Registering class org.keycloak.broker.provider.mappersync.ConfigSyncEventListener
2024-10-23 01:50:14,726 INFO [org.keycloak.services] (main) KC-SERVICES0050: Initializing master realm
2024-10-23 01:50:15,746 INFO [org.keycloak.services] (main) KC-SERVICES0077: Created temporary admin user with username admin
2024-10-23 01:50:15,765 WARN [io.agroal.pool] (main) Datasource '<default>': JDBC resources leaked: 1 ResultSet(s) and 0 Statement(s)
2024-10-23 01:50:15,836 INFO [io.quarkus] (main) Keycloak 26.0.0 on JVM (powered by Quarkus 3.15.1) started in 5.722s. Listening on: http://0.0.0.0:8080
2024-10-23 01:50:15,837 INFO [io.quarkus] (main) Profile dev activated.
2024-10-23 01:50:15,837 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, keycloak, narayana-jta, opentelemetry, reactive-routes, rest, rest-jackson, smallrye-context-propagation, vertx]
2024-10-23 01:50:15,841 WARN [org.keycloak.quarkus.runtime.KeycloakMain] (main) Running the server in development mode. DO NOT use this configuration in production.
The env or printenv command can then be run in the pod to ensure the pod contains the KC_BOOTSTRAP_ADMIN_USERNAME and KC_BOOTSTRAP_ADMIN_PASSWORD variables.
[c065234@DLOCPLJ-1-0001 ~]$ oc exec pod/keycloak-1-qzbvs -- env
KC_BOOTSTRAP_ADMIN_USERNAME=admin
KC_BOOTSTRAP_ADMIN_PASSWORD=itsasecret
KC_PROXY_HEADERS=xforwarded
Keycloak Service
Next let's create a file named keycloak_service.yml that contains the following YAML.
apiVersion: v1
kind: Service
metadata:
name: keycloak
namespace: keycloak
spec:
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- port: 8080
protocol: TCP
targetPort: 8080
selector:
application: keycloak
sessionAffinity: None
type: ClusterIP
And then the oc apply command can be used to create the service.
oc apply --filename keycloak_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 svc
Name: keycloak
Namespace: keycloak
Labels: <none>
Annotations: <none>
Selector: application=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:
application: 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 keycloak_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.
~]$ oc describe route keycloak
Name: keycloak
Namespace: keycloak
Created: 7 days ago
Labels: application=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!

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