OpenShift Deployment Guide

November 21, 2025 · View on GitHub

Overview

This guide explains how to deploy the Keycloak MCP Server container to OpenShift using various methods.

Prerequisites

  • OpenShift cluster access (v4.x or later)
  • oc CLI installed and configured
  • Appropriate permissions to create resources in a namespace
  • Keycloak instance with HTTPS enabled (production requirement)
  • Keycloak CA certificate (if using self-signed certificates)

Important: User-Based Authentication (v0.4.0+)

** NEW: The MCP Server uses JWT Bearer tokens - each user authenticates with their OWN credentials!**

See authentication.md for complete authentication guide

How it works:

User → Gets JWT token from Keycloak

User → MCP Server (with JWT in headers)

MCP Server → Validates token (OIDC)

MCP Server → Keycloak API (with user's token)

Keycloak → Enforces user's permissions

Benefits:

  • Each user has their own permissions (no shared admin access)
  • Keycloak enforces its existing permission system
  • Realm isolation works automatically
  • Role-based access control (admin/manager/viewer)
  • Full audit trail by user
  • No service account needed!

Getting your token:

./scripts/get-mcp-token.sh \
 --keycloak-url https://keycloak.example.com \
 --username your-username \
 --password your-password

Important: HTTPS Keycloak Configuration

** Production Keycloak always uses HTTPS.** Before deploying, configure TLS trust:

See keycloak-tls-setup.md for complete TLS configuration guide

Quick summary:

  • Trusted CA: No additional configuration needed
  • Self-signed: Extract and mount CA certificate
  • OpenShift Service CA: Use ConfigMap annotation for auto-injection
  • Keycloak Operator: Extract certificate from Keycloak TLS secret

Reference: Keycloak Operator Installation Tutorial

Quick Start

# 1. Login to OpenShift
oc login --server=https://your-openshift-cluster:6443

# 2. Create a new project
oc new-project keycloak-mcp

# 3. Deploy using manifests
oc apply -f deploy/openshift/

# 4. Get the route URL
oc get route keycloak-mcp-server -o jsonpath='{.spec.host}'

Method 2: Using Kustomize

# Deploy with kustomize
oc apply -k deploy/openshift/

# Check deployment status
oc get pods -l app=keycloak-mcp-server

Method 3: Using OpenShift Web Console

  1. Log in to OpenShift Web Console
  2. Navigate to Developer perspective
  3. Click +AddImport from Git or Container Image
  4. Use image: quay.io/sshaaf/keycloak-mcp-server:latest
  5. Configure environment variables and create

Detailed Deployment

Step 1: Create Project/Namespace

# Create a dedicated project
oc new-project keycloak-mcp

# Or use existing project
oc project keycloak-mcp

Step 2: Configure Keycloak Connection

** Important: Use HTTPS URL for production Keycloak**

Update ConfigMap (deploy/openshift/configmap.yaml):

data:
 # Production Keycloak HTTPS URL
 keycloak-url: "https://keycloak.rhbk.apps.example.com"
 # OR if using internal service (with custom CA)
 # keycloak-url: "https://keycloak.keycloak-system.svc.cluster.local"

Note: No secret is required. The MCP server validates JWT tokens using public OIDC discovery.

Previous versions required a secret, but with JWT authentication, no secrets are needed for deployment.

Environment Variables (in deploy/openshift/deployment.yaml):

stringData:
 username: "your-keycloak-admin"
 password: "your-keycloak-password"

Or create Secret from command line:

oc create secret generic keycloak-mcp-secret \
 --from-literal=username=admin \
 --from-literal=password='YourSecurePassword123!'

If using self-signed certificates, configure CA trust:

# Extract CA certificate from Keycloak TLS secret
oc get secret example-tls-secret -n rhbk \
 -o jsonpath='{.data.tls\.crt}' | base64 -d > keycloak-ca.crt

# Create CA ConfigMap
oc create configmap keycloak-ca-bundle \
 --from-file=ca.crt=keycloak-ca.crt

# See keycloak-tls-setup.md for complete configuration

Step 3: Apply Manifests

# Apply all resources
oc apply -f deploy/openshift/configmap.yaml
oc apply -f deploy/openshift/deployment.yaml
oc apply -f deploy/openshift/service.yaml
oc apply -f deploy/openshift/route.yaml

Or apply all at once:

oc apply -f deploy/openshift/

Step 4: Verify Deployment

# Check pods
oc get pods -l app=keycloak-mcp-server

# Check deployment
oc get deployment keycloak-mcp-server

# Check service
oc get svc keycloak-mcp-server

# Check route
oc get route keycloak-mcp-server

# View logs
oc logs -f deployment/keycloak-mcp-server

Step 5: Access the Application

# Get the route URL
ROUTE_URL=$(oc get route keycloak-mcp-server -o jsonpath='{.spec.host}')
echo "Application URL: https://${ROUTE_URL}"

# Test SSE endpoint
curl https://${ROUTE_URL}/mcp/sse

Deployment Options

Option 1: Using Specific Image Tag

Deploy with a specific git commit SHA for production:

# Edit deployment.yaml or use kustomize
oc set image deployment/keycloak-mcp-server \
 keycloak-mcp-server=quay.io/sshaaf/keycloak-mcp-server:49ff54e

Option 2: Using Helm (if available)

Create a Helm chart for more flexibility:

# Install using Helm
helm install keycloak-mcp-server ./charts/keycloak-mcp-server \
 --set image.tag=0.3.0 \
 --set keycloak.url=http://keycloak:8080 \
 --set keycloak.username=admin \
 --set keycloak.password=admin

Option 3: Using OpenShift Template

Create and process an OpenShift template:

oc process -f deploy/openshift/template.yaml \
 -p KEYCLOAK_URL=http://keycloak:8080 \
 -p KEYCLOAK_USERNAME=admin \
 -p KEYCLOAK_PASSWORD=admin \
 | oc apply -f -

Configuration

Environment Variables

The deployment supports the following environment variables:

VariableDescriptionDefaultRequired
KC_URLKeycloak server URL-Yes
KC_USERKeycloak admin username-Yes
KC_PASSWORDKeycloak admin password-Yes
QUARKUS_HTTP_PORTHTTP port8080No
QUARKUS_LOG_LEVELLog levelINFONo

Resource Limits

Default resource configuration:

resources:
 requests:
 memory: "256Mi"
 cpu: "100m"
 limits:
 memory: "512Mi"
 cpu: "500m"

Adjust for your workload:

oc set resources deployment/keycloak-mcp-server \
 --requests=cpu=200m,memory=512Mi \
 --limits=cpu=1000m,memory=1Gi

Scaling

# Scale up
oc scale deployment/keycloak-mcp-server --replicas=3

# Enable autoscaling
oc autoscale deployment/keycloak-mcp-server \
 --min=1 --max=5 --cpu-percent=80

Security

Running as Non-Root

The container runs as non-root by default. To enforce:

spec:
 template:
 spec:
 securityContext:
 runAsNonRoot: true
 seccompProfile:
 type: RuntimeDefault
 containers:
 - name: keycloak-mcp-server
 securityContext:
 allowPrivilegeEscalation: false
 capabilities:
 drop:
 - ALL

Using Sealed Secrets

For production, use Sealed Secrets or External Secrets Operator:

# Create sealed secret
echo -n 'admin' | \
 kubectl create secret generic keycloak-mcp-secret \
 --dry-run=client \
 -o yaml | \
 kubeseal -o yaml > sealed-configmap.yaml

oc apply -f sealed-configmap.yaml

Network Policies

Restrict network access:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
 name: keycloak-mcp-server-netpol
spec:
 podSelector:
 matchLabels:
 app: keycloak-mcp-server
 policyTypes:
 - Ingress
 - Egress
 ingress:
 - from:
 - namespaceSelector:
 matchLabels:
 name: openshift-ingress
 ports:
 - protocol: TCP
 port: 8080
 egress:
 - to:
 - namespaceSelector:
 matchLabels:
 name: keycloak-system
 ports:
 - protocol: TCP
 port: 8080

Health Checks

The deployment includes comprehensive health checks:

Liveness Probe

  • Path: /q/health/live
  • Checks if the application is alive
  • Restarts pod if failing

Readiness Probe

  • Path: /q/health/ready
  • Checks if application can accept traffic
  • Removes from service if failing

Startup Probe

  • Path: /q/health/started
  • Checks if application has started
  • Gives more time for slow startup

Monitoring

Viewing Logs

# Stream logs
oc logs -f deployment/keycloak-mcp-server

# Last 100 lines
oc logs --tail=100 deployment/keycloak-mcp-server

# Previous container (if crashed)
oc logs deployment/keycloak-mcp-server --previous

Metrics (if Prometheus is installed)

# Check metrics endpoint
oc exec deployment/keycloak-mcp-server -- \
 curl http://localhost:8080/q/metrics

# Create ServiceMonitor for Prometheus
cat <<EOF | oc apply -f -
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
 name: keycloak-mcp-server
 labels:
 app: keycloak-mcp-server
spec:
 selector:
 matchLabels:
 app: keycloak-mcp-server
 endpoints:
 - port: http
 path: /q/metrics
 interval: 30s
EOF

Troubleshooting

Pod Not Starting

# Check pod status
oc get pods -l app=keycloak-mcp-server

# Describe pod for events
oc describe pod -l app=keycloak-mcp-server

# Check logs
oc logs -l app=keycloak-mcp-server

Connection to Keycloak Fails

# Test from pod
oc exec deployment/keycloak-mcp-server -- \
 curl -v $KC_URL

# Check DNS resolution
oc exec deployment/keycloak-mcp-server -- \
 nslookup keycloak.keycloak-system.svc.cluster.local

# Check network policy
oc get networkpolicy

Image Pull Errors

# Check image pull secret (if using private registry)
oc get secret -n keycloak-mcp

# Create image pull secret
oc create secret docker-registry quay-pull-secret \
 --docker-server=quay.io \
 --docker-username=sshaaf \
 --docker-password=<token>

# Link to service account
oc secrets link default quay-pull-secret --for=pull

Route Not Accessible

# Check route
oc get route keycloak-mcp-server

# Check if TLS is configured
oc describe route keycloak-mcp-server

# Test from inside cluster
oc run test --rm -it --image=curlimages/curl -- \
 curl http://keycloak-mcp-server/mcp/sse

Advanced Configurations

Using External Keycloak

If Keycloak is outside OpenShift (e.g., managed service, different cluster):

# configmap.yaml
data:
 # External Keycloak with trusted CA certificate
 keycloak-url: "https://keycloak.example.com"

If using custom CA or self-signed certificate:

  1. Get the CA certificate from your Keycloak administrator
  2. Create ConfigMap:
oc create configmap keycloak-ca-bundle --from-file=ca.crt=external-keycloak-ca.crt
  1. Update deployment to mount the certificate (see keycloak-tls-setup.md)

If using Keycloak Operator (recommended approach from this tutorial):

# Keycloak is deployed with operator in 'rhbk' namespace
# Extract the TLS certificate
oc get secret example-tls-secret -n rhbk \
 -o jsonpath='{.data.tls\.crt}' | base64 -d > keycloak-ca.crt

# Create CA bundle in MCP namespace
oc create configmap keycloak-ca-bundle \
 --from-file=ca.crt=keycloak-ca.crt \
 -n keycloak-mcp

Blue-Green Deployment

# Create blue deployment
oc apply -f deploy/openshift/ -l version=blue

# Test blue
oc get route keycloak-mcp-server-blue

# Deploy green
oc apply -f deploy/openshift/ -l version=green

# Switch traffic
oc patch route keycloak-mcp-server \
 -p '{"spec":{"to":{"name":"keycloak-mcp-server-green"}}}'

Using S2I (Source-to-Image)

Build from source using OpenShift S2I:

oc new-app quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21~https://github.com/sshaaf/keycloak-mcp-server \
 --name=keycloak-mcp-server \
 --env KC_URL=http://keycloak:8080 \
 --env KC_USER=admin \
 --env KC_PASSWORD=admin

CI/CD Integration

Using Tekton Pipelines

Create a Tekton pipeline for automated deployment:

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
 name: keycloak-mcp-deploy
spec:
 params:
 - name: image-tag
 description: Image tag to deploy
 default: latest
 tasks:
 - name: deploy
 taskRef:
 name: openshift-client
 params:
 - name: SCRIPT
 value: |
 oc set image deployment/keycloak-mcp-server \
 keycloak-mcp-server=quay.io/sshaaf/keycloak-mcp-server:$(params.image-tag)
 oc rollout status deployment/keycloak-mcp-server

Using ArgoCD

Deploy using GitOps with ArgoCD:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
 name: keycloak-mcp-server
 namespace: argocd
spec:
 project: default
 source:
 repoURL: https://github.com/sshaaf/keycloak-mcp-server
 targetRevision: main
 path: deploy/openshift
 destination:
 server: https://kubernetes.default.svc
 namespace: keycloak-mcp
 syncPolicy:
 automated:
 prune: true
 selfHeal: true

Backup and Restore

Backup Resources

# Backup all resources
oc get all,configmap,secret -l app=keycloak-mcp-server -o yaml > backup.yaml

# Backup specific resources
oc get deployment,service,route keycloak-mcp-server -o yaml > resources-backup.yaml

Restore

# Restore from backup
oc apply -f backup.yaml

Cleanup

# Delete all resources
oc delete -f deploy/openshift/

# Or delete by label
oc delete all -l app=keycloak-mcp-server

# Delete project
oc delete project keycloak-mcp

Best Practices

1. Use Specific Image Tags

Don't use latest in production:

image: quay.io/sshaaf/keycloak-mcp-server:latest

Use specific git commit SHA or version:

image: quay.io/sshaaf/keycloak-mcp-server:49ff54e

2. Resource Limits

Always set resource limits to prevent resource exhaustion:

resources:
 requests:
 memory: "256Mi"
 cpu: "100m"
 limits:
 memory: "512Mi"
 cpu: "500m"

3. Health Checks

Include comprehensive health checks for reliability:

livenessProbe: ...
readinessProbe: ...
startupProbe: ...

4. Security

  • Run as non-root user
  • Drop all capabilities
  • Use read-only root filesystem where possible
  • Implement network policies
  • Use secrets for sensitive data

5. High Availability

  • Use multiple replicas (2-3 minimum)
  • Configure pod disruption budgets
  • Distribute across availability zones
spec:
 replicas: 3
 template:
 spec:
 affinity:
 podAntiAffinity:
 preferredDuringSchedulingIgnoredDuringExecution:
 - weight: 100
 podAffinityTerm:
 labelSelector:
 matchLabels:
 app: keycloak-mcp-server
 topologyKey: kubernetes.io/hostname

Summary

Quick deployment with pre-configured manifests Secure by default with non-root containers Production-ready with health checks and resource limits HTTPS configured for secure Keycloak communication OpenShift native using Routes and SecurityContextConstraints Flexible supporting multiple deployment methods Scalable with horizontal pod autoscaling support

Key Configuration Files

FilePurpose
deployment.yamlMain application deployment with TLS support
service.yamlClusterIP service definition
route.yamlOpenShift route for external access
configmap.yamlKeycloak URL and OIDC client configuration
ca-configmap.yamlCA certificate for self-signed Keycloak TLS
kustomization.yamlKustomize configuration
keycloak-tls-setup.mdComplete TLS/HTTPS configuration guide

Important Notes

Production Keycloak uses HTTPS only - Configure TLS trust before deploying Read keycloak-tls-setup.md for TLS configuration Keycloak Operator: See installation tutorial Use specific image tags in production (not latest)


Your Keycloak MCP Server is now ready for OpenShift!

For issues or questions: