Overview
Deploy the MCP Server with LangGraph on Kubernetes for production-grade reliability, scalability, and high availability. This guide covers deployment with Keycloak SSO, Redis sessions, OpenFGA authorization, and observability.v2.4.0 includes updated infrastructure (OpenFGA v1.10.2, Keycloak 26.4.0, PostgreSQL 16), production-ready Kubernetes manifests with Keycloak, Redis, and comprehensive security configurations.
Architecture
Prerequisites
1
Kubernetes Cluster
Minimum requirements:
- Kubernetes 1.25+
- 4 vCPUs, 8GB RAM
- 50GB storage
- kubectl configured
- 8+ vCPUs, 16GB+ RAM
- 100GB+ SSD storage
- Multi-zone cluster
- Auto-scaling enabled
2
Container Registry
Build and push the Docker image:Update
Copy
Ask AI
# Build image
docker build -t gcr.io/your-project/mcp-server-langgraph:v2.4.0 .
# Push to registry
docker push gcr.io/your-project/mcp-server-langgraph:v2.4.0
deployments/kubernetes/base/deployment.yaml:Copy
Ask AI
spec:
template:
spec:
containers:
- name: mcp-server-langgraph
image: gcr.io/your-project/mcp-server-langgraph:v2.4.0
3
Install Tools
Copy
Ask AI
# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl && sudo mv kubectl /usr/local/bin/
# kustomize (optional)
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
sudo mv kustomize /usr/local/bin/
# helm (for dependencies)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
Quick Deploy
Copy
Ask AI
## 1. Create namespace
kubectl apply -f deployments/kubernetes/base/namespace.yaml
## 2. Create secrets
kubectl create secret generic mcp-server-langgraph-secrets \
--namespace=mcp-server-langgraph \
--from-literal=anthropic-api-key="${ANTHROPIC_API_KEY}" \
--from-literal=google-api-key="${GOOGLE_API_KEY}" \
--from-literal=jwt-secret-key="$(openssl rand -base64 32)" \
--from-literal=keycloak-client-secret="${KEYCLOAK_CLIENT_SECRET}" \
--from-literal=redis-password="$(openssl rand -base64 32)" \
--from-literal=openfga-store-id="${OPENFGA_STORE_ID}" \
--from-literal=openfga-model-id="${OPENFGA_MODEL_ID}"
## 3. Deploy dependencies (Keycloak, Redis, OpenFGA)
kubectl apply -f deployments/kubernetes/base/keycloak-deployment.yaml
kubectl apply -f deployments/kubernetes/base/keycloak-service.yaml
kubectl apply -f deployments/kubernetes/base/redis-session-deployment.yaml
kubectl apply -f deployments/kubernetes/base/redis-session-service.yaml
## Note: OpenFGA deployment not shown - use Helm chart or manifest
## 4. Wait for dependencies
kubectl wait --for=condition=available --timeout=300s \
deployment/keycloak -n mcp-server-langgraph
kubectl wait --for=condition=available --timeout=300s \
deployment/redis-session -n mcp-server-langgraph
## 5. Deploy application
kubectl apply -f deployments/kubernetes/base/
## 6. Wait for rollout
kubectl rollout status deployment/mcp-server-langgraph -n mcp-server-langgraph
## 7. Verify
kubectl get pods -n mcp-server-langgraph
kubectl logs -l app=mcp-server-langgraph -n mcp-server-langgraph --tail=50
Step-by-Step Deployment
1. Namespace and RBAC
Copy
Ask AI
## Create namespace
kubectl apply -f - << 'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: mcp-server-langgraph
labels:
name: mcp-server-langgraph
environment: production
EOF
## Create service account
kubectl apply -f deployments/kubernetes/base/serviceaccount.yaml
2. Configuration
ConfigMap for application configuration:Copy
Ask AI
kubectl apply -f deployments/kubernetes/base/configmap.yaml
View configmap.yaml
View configmap.yaml
Copy
Ask AI
apiVersion: v1
kind: ConfigMap
metadata:
name: mcp-server-langgraph-config
namespace: mcp-server-langgraph
data:
# Service
environment: "production"
log_level: "INFO"
# LLM
llm_provider: "anthropic"
model_name: "claude-sonnet-4-5-20250929"
enable_fallback: "true"
# Authentication
auth_provider: "keycloak"
auth_mode: "session"
# Keycloak
keycloak_server_url: "http://keycloak:8080"
keycloak_realm: "mcp-server-langgraph"
keycloak_client_id: "langgraph-client"
# Redis Sessions
session_backend: "redis"
redis_url: "redis://redis-session:6379/0"
session_ttl_seconds: "86400"
# OpenFGA
openfga_api_url: "http://openfga:8080"
Copy
Ask AI
## Generate secrets
export JWT_SECRET=$(openssl rand -base64 32)
export REDIS_PASSWORD=$(openssl rand -base64 32)
## Create secret
kubectl create secret generic mcp-server-langgraph-secrets \
--namespace=mcp-server-langgraph \
--from-literal=anthropic-api-key="${ANTHROPIC_API_KEY}" \
--from-literal=google-api-key="${GOOGLE_API_KEY}" \
--from-literal=openai-api-key="${OPENAI_API_KEY}" \
--from-literal=jwt-secret-key="${JWT_SECRET}" \
--from-literal=keycloak-client-secret="${KEYCLOAK_CLIENT_SECRET}" \
--from-literal=redis-password="${REDIS_PASSWORD}" \
--from-literal=openfga-store-id="${OPENFGA_STORE_ID}" \
--from-literal=openfga-model-id="${OPENFGA_MODEL_ID}"
Never commit secrets to Git! Use external secret managers (see Infisical Setup) or cloud-native solutions (GCP Secret Manager, AWS Secrets Manager, Azure Key Vault).
3. Deploy Keycloak
Copy
Ask AI
## Deploy Keycloak with PostgreSQL
helm install keycloak bitnami/keycloak \
--namespace mcp-server-langgraph \
--set auth.adminUser=admin \
--set auth.adminPassword="$(openssl rand -base64 32)" \
--set postgresql.enabled=true \
--set postgresql.auth.password="$(openssl rand -base64 32)" \
--set replicaCount=2 \
--set ingress.enabled=true \
--set ingress.hostname=sso.yourdomain.com
## Wait for deployment
kubectl wait --for=condition=available --timeout=300s \
deployment/keycloak -n mcp-server-langgraph
## Initialize Keycloak
python scripts/setup/setup_keycloak.py
## Save client secret to Kubernetes secret
kubectl patch secret mcp-server-langgraph-secrets \
-n mcp-server-langgraph \
--patch "{\"data\":{\"keycloak-client-secret\":\"$(echo -n $CLIENT_SECRET | base64)\"}}"
4. Deploy Redis
Copy
Ask AI
## Deploy Redis with replication
helm install redis-session bitnami/redis \
--namespace mcp-server-langgraph \
--set auth.password="${REDIS_PASSWORD}" \
--set master.persistence.enabled=true \
--set master.persistence.size=10Gi \
--set replica.replicaCount=2 \
--set replica.persistence.enabled=true \
--set sentinel.enabled=true
## Wait for deployment
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/name=redis \
-n mcp-server-langgraph \
--timeout=300s
## Test connection
kubectl run redis-test --rm -i --tty \
--image redis:7-alpine \
--namespace mcp-server-langgraph \
-- redis-cli -h redis-session -a $REDIS_PASSWORD ping
## Expected: PONG
5. Deploy OpenFGA
Copy
Ask AI
## Deploy OpenFGA with PostgreSQL
kubectl apply -f - << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: openfga
namespace: mcp-server-langgraph
spec:
replicas: 2
selector:
matchLabels:
app: openfga
template:
metadata:
labels:
app: openfga
spec:
containers:
- name: openfga
image: openfga/openfga:v1.10.2
args:
- run
env:
- name: OPENFGA_DATASTORE_ENGINE
value: postgres
- name: OPENFGA_DATASTORE_URI
value: "postgres://openfga:password@postgres:5432/openfga"
ports:
- containerPort: 8080
name: http
- containerPort: 8081
name: grpc
---
apiVersion: v1
kind: Service
metadata:
name: openfga
namespace: mcp-server-langgraph
spec:
selector:
app: openfga
ports:
- name: http
port: 8080
targetPort: 8080
- name: grpc
port: 8081
targetPort: 8081
EOF
## Initialize OpenFGA
python scripts/setup/setup_openfga.py
6. Deploy Application
Copy
Ask AI
## Deploy all manifests
kubectl apply -f deployments/kubernetes/base/deployment.yaml
kubectl apply -f deployments/kubernetes/base/service.yaml
kubectl apply -f deployments/kubernetes/base/hpa.yaml
kubectl apply -f deployments/kubernetes/base/pdb.yaml
kubectl apply -f deployments/kubernetes/base/ingress-http.yaml
## Wait for rollout
kubectl rollout status deployment/mcp-server-langgraph -n mcp-server-langgraph
## Check pods
kubectl get pods -n mcp-server-langgraph -l app=mcp-server-langgraph
7. Configure Ingress
- NGINX Ingress
- GKE Ingress
- AWS ALB
Copy
Ask AI
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mcp-server-langgraph
namespace: mcp-server-langgraph
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.yourdomain.com
secretName: mcp-server-langgraph-tls
rules:
- host: api.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mcp-server-langgraph
port:
number: 8000
Copy
Ask AI
kubectl apply -f ingress.yaml
High Availability
Horizontal Pod Autoscaling
Copy
Ask AI
## deployments/kubernetes/base/hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mcp-server-langgraph
namespace: mcp-server-langgraph
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mcp-server-langgraph
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100
periodSeconds: 60
Pod Disruption Budget
Copy
Ask AI
## deployments/kubernetes/base/pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: mcp-server-langgraph
namespace: mcp-server-langgraph
spec:
minAvailable: 2
selector:
matchLabels:
app: mcp-server-langgraph
Pod Anti-Affinity
Spread pods across availability zones:Copy
Ask AI
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- mcp-server-langgraph
topologyKey: topology.kubernetes.io/zone
Security
Network Policies
Copy
Ask AI
## deployments/kubernetes/base/networkpolicy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mcp-server-langgraph
namespace: mcp-server-langgraph
spec:
podSelector:
matchLabels:
app: mcp-server-langgraph
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8000
egress:
- to:
- podSelector:
matchLabels:
app: keycloak
ports:
- protocol: TCP
port: 8080
- to:
- podSelector:
matchLabels:
app: redis-session
ports:
- protocol: TCP
port: 6379
- to:
- podSelector:
matchLabels:
app: openfga
ports:
- protocol: TCP
port: 8080
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443 # HTTPS for LLM APIs
Pod Security Standards
Copy
Ask AI
apiVersion: v1
kind: Namespace
metadata:
name: mcp-server-langgraph
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
Security Context
Copy
Ask AI
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
Observability
Prometheus Metrics
Copy
Ask AI
## Pod annotations for Prometheus scraping
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8000"
prometheus.io/path: "/metrics/prometheus"
Copy
Ask AI
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace observability --create-namespace
OpenTelemetry
Deploy OpenTelemetry Collector:Copy
Ask AI
helm install opentelemetry-collector open-telemetry/opentelemetry-collector \
--namespace observability \
--set mode=deployment \
--set config.exporters.otlp.endpoint=jaeger:4317
Jaeger Tracing
Copy
Ask AI
helm install jaeger jaegertracing/jaeger \
--namespace observability \
--set provisionDataStore.cassandra=false \
--set allInOne.enabled=true \
--set storage.type=memory
Copy
Ask AI
kubectl port-forward svc/jaeger-query 16686:16686 -n observability
## Open: http://localhost:16686
Health Checks
The deployment includes comprehensive health checks:Copy
Ask AI
startupProbe:
httpGet:
path: /health/startup
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 30
livenessProbe:
httpGet:
path: /health
port: 8000
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 8000
periodSeconds: 5
failureThreshold: 3
Copy
Ask AI
kubectl exec -it deploy/mcp-server-langgraph -n mcp-server-langgraph -- \
curl http://localhost:8000/health
Troubleshooting
Pods not starting
Pods not starting
Copy
Ask AI
# Check pod status
kubectl get pods -n mcp-server-langgraph
# Describe pod
kubectl describe pod POD_NAME -n mcp-server-langgraph
# Check logs
kubectl logs POD_NAME -n mcp-server-langgraph
# Check events
kubectl get events -n mcp-server-langgraph --sort-by='.lastTimestamp'
Init containers failing
Init containers failing
Copy
Ask AI
# Check init container logs
kubectl logs POD_NAME -n mcp-server-langgraph -c wait-for-keycloak
kubectl logs POD_NAME -n mcp-server-langgraph -c wait-for-redis
kubectl logs POD_NAME -n mcp-server-langgraph -c wait-for-openfga
# Test connectivity manually
kubectl run test --rm -it --image=busybox -- sh
nc -zv keycloak 8080
nc -zv redis-session 6379
nc -zv openfga 8080
Health checks failing
Health checks failing
Copy
Ask AI
# Check readiness
kubectl get pods -n mcp-server-langgraph -o wide
# Test health endpoint
kubectl exec -it deploy/mcp-server-langgraph -n mcp-server-langgraph -- \
curl -v http://localhost:8000/health
# Check dependencies
kubectl logs deploy/mcp-server-langgraph -n mcp-server-langgraph | grep -i error
High memory usage
High memory usage
Copy
Ask AI
# Check resource usage
kubectl top pods -n mcp-server-langgraph
# View metrics
kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/mcp-server-langgraph/pods
# Adjust limits in deployment.yaml
resources:
limits:
memory: 4Gi # Increase limit
Next Steps
Helm Deployment
Deploy using Helm charts
Production Checklist
Pre-deployment verification
Scaling Guide
Auto-scaling and performance tuning
Disaster Recovery
Backup and restore procedures
Production Ready: Your MCP Server is now running on Kubernetes with high availability and enterprise-grade security!