Skip to main content

Kubernetes Deployment

Deploy OpenPrime to Kubernetes using Helm for scalable, production-grade deployments.

Prerequisites​

  • Kubernetes cluster 1.25+
  • Helm 3.x
  • kubectl configured
  • Ingress controller (nginx-ingress recommended)
  • cert-manager (for TLS)
  • PostgreSQL (managed or in-cluster)

Helm Chart Installation​

Add Helm Repository​

helm repo add openprime https://charts.openprime.dev
helm repo update

Install with Default Values​

helm install openprime openprime/openprime \
--namespace openprime \
--create-namespace

Install with Custom Values​

helm install openprime openprime/openprime \
--namespace openprime \
--create-namespace \
-f values.yaml

Helm Values Configuration​

Complete values.yaml​

# Global configuration
global:
domain: yourdomain.com
environment: production

# Frontend configuration
frontend:
replicaCount: 2
image:
repository: openprime/frontend
tag: latest
pullPolicy: IfNotPresent
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
env:
REACT_APP_API_URL: https://api.yourdomain.com
REACT_APP_KEYCLOAK_URL: https://auth.yourdomain.com
REACT_APP_KEYCLOAK_REALM: openprime
REACT_APP_KEYCLOAK_CLIENT_ID: openprime-app

# Backend configuration
backend:
replicaCount: 3
image:
repository: openprime/backend
tag: latest
pullPolicy: IfNotPresent
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: 1000m
memory: 1Gi
env:
NODE_ENV: production
LOG_LEVEL: info
secrets:
encryptionKey: "" # Set via --set or external secret

# Database configuration
postgresql:
enabled: true # Set false if using external database
auth:
username: openprime
password: "" # Set via --set
database: openprime
primary:
persistence:
size: 20Gi
# External database settings (if enabled: false)
external:
host: ""
port: 5432
database: openprime
existingSecret: ""

# Keycloak configuration
keycloak:
enabled: true # Set false if using external Keycloak
auth:
adminUser: admin
adminPassword: "" # Set via --set
postgresql:
enabled: true
# External Keycloak settings (if enabled: false)
external:
url: ""
realm: openprime

# Injecto service
injecto:
replicaCount: 2
image:
repository: openprime/injecto
tag: latest
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi

# Ingress configuration
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
hosts:
- host: yourdomain.com
paths:
- path: /
service: frontend
- host: api.yourdomain.com
paths:
- path: /
service: backend
- host: auth.yourdomain.com
paths:
- path: /
service: keycloak
tls:
- secretName: openprime-tls
hosts:
- yourdomain.com
- api.yourdomain.com
- auth.yourdomain.com

# Autoscaling
autoscaling:
enabled: true
frontend:
minReplicas: 2
maxReplicas: 10
targetCPUUtilization: 70
backend:
minReplicas: 3
maxReplicas: 20
targetCPUUtilization: 70

# Pod disruption budgets
podDisruptionBudget:
frontend:
minAvailable: 1
backend:
minAvailable: 2

# Service accounts
serviceAccount:
create: true
name: openprime

# Network policies
networkPolicy:
enabled: true

Deployment Steps​

1. Create Namespace​

kubectl create namespace openprime

2. Create Secrets​

# Database password
kubectl create secret generic openprime-db \
--namespace openprime \
--from-literal=password=your-db-password

# Encryption key
kubectl create secret generic openprime-encryption \
--namespace openprime \
--from-literal=key=$(openssl rand -hex 32)

# Keycloak admin password
kubectl create secret generic keycloak-admin \
--namespace openprime \
--from-literal=password=your-keycloak-password

3. Install Chart​

helm install openprime openprime/openprime \
--namespace openprime \
-f values.yaml \
--set postgresql.auth.existingSecret=openprime-db \
--set backend.secrets.existingSecret=openprime-encryption \
--set keycloak.auth.existingSecret=keycloak-admin

4. Verify Installation​

# Check pods
kubectl get pods -n openprime

# Check services
kubectl get svc -n openprime

# Check ingress
kubectl get ingress -n openprime

External Database​

For managed PostgreSQL (RDS, Cloud SQL, etc.):

# values.yaml
postgresql:
enabled: false
external:
host: your-db-host.rds.amazonaws.com
port: 5432
database: openprime
existingSecret: openprime-db-external

# Create secret
kubectl create secret generic openprime-db-external \
--namespace openprime \
--from-literal=username=openprime \
--from-literal=password=your-db-password

External Keycloak​

For existing Keycloak instance:

# values.yaml
keycloak:
enabled: false
external:
url: https://your-keycloak.com
realm: openprime

High Availability Configuration​

Multi-Zone Deployment​

# values.yaml
frontend:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: topology.kubernetes.io/zone

backend:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
topologyKey: topology.kubernetes.io/zone

Resource Quotas​

apiVersion: v1
kind: ResourceQuota
metadata:
name: openprime-quota
namespace: openprime
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi

Monitoring​

Prometheus ServiceMonitor​

# values.yaml
serviceMonitor:
enabled: true
interval: 30s
labels:
release: prometheus

Grafana Dashboard​

Import the OpenPrime dashboard from the Helm chart:

kubectl get configmap openprime-grafana-dashboard -n openprime -o yaml

Upgrades​

Upgrade Chart​

# Update repo
helm repo update

# Upgrade release
helm upgrade openprime openprime/openprime \
--namespace openprime \
-f values.yaml

# Rollback if needed
helm rollback openprime 1 -n openprime

Database Migrations​

Migrations run automatically on backend pod startup. For manual execution:

kubectl exec -it deploy/openprime-backend -n openprime -- \
npm run db:migrate

Troubleshooting​

Check Pod Logs​

kubectl logs -f deploy/openprime-backend -n openprime
kubectl logs -f deploy/openprime-frontend -n openprime

Debug Pod​

kubectl run debug --rm -it --image=alpine -n openprime -- sh

Check Events​

kubectl get events -n openprime --sort-by='.lastTimestamp'