Skip to content

Kubernetes (Helm)

Deploy jitsudo to Kubernetes using the official Helm chart. The chart includes an optional PostgreSQL subchart (bitnami) and supports external managed databases for production.

  • Kubernetes 1.25+
  • Helm 3.10+
  • An OIDC provider (Okta, Entra ID, Keycloak, etc.) — see OIDC Integration
  • A PostgreSQL database (use the subchart for testing, an external RDS/Cloud SQL for production)
Terminal window
# Clone the repo and install from the local chart
git clone https://github.com/jitsudo-dev/jitsudo.git
cd jitsudo
Terminal window
helm upgrade --install jitsudo ./helm/jitsudo \
--namespace jitsudo \
--create-namespace \
--set config.auth.oidcIssuer=https://your-idp.example.com \
--set config.auth.clientId=jitsudo-server

This uses the bundled PostgreSQL subchart with default credentials. Do not use this in production.

For production, disable the bundled PostgreSQL and supply your own database credentials via a Kubernetes Secret:

Terminal window
# 1. Create a secret with the database URL
kubectl create secret generic jitsudo-db \
--namespace jitsudo \
--from-literal=DATABASE_URL="postgres://jitsudo:STRONG_PASSWORD@your-db.example.com:5432/jitsudo?sslmode=require"
# 2. Install the chart
helm upgrade --install jitsudo ./helm/jitsudo \
--namespace jitsudo \
--create-namespace \
--values values-prod.yaml

values-prod.yaml:

config:
auth:
oidcIssuer: "https://your-idp.example.com"
clientId: "jitsudo-server"
database:
existingSecret: "jitsudo-db" # Secret with DATABASE_URL key
providers:
aws:
enabled: true
region: "us-east-1"
roleArnTemplate: "arn:aws:iam::{scope}:role/jitsudo-{role}"
maxDuration: "4h"
log:
level: "info"
format: "json"
# Disable bundled PostgreSQL — use external managed database
postgresql:
enabled: false
# Enable ingress for the REST gateway
ingress:
enabled: true
className: "nginx"
hosts:
- host: jitsudo.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: jitsudo-tls
hosts:
- jitsudo.example.com
# Production resource sizing
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
memory: 512Mi
# Enable horizontal autoscaling
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 70
# Enable pod disruption budget for rolling updates
podDisruptionBudget:
enabled: true
minAvailable: 1

For production high-availability, apply the bundled values-ha.yaml overlay. It enables 2 replicas, HPA (CPU + memory), PodDisruptionBudget, pod anti-affinity, and a PostgreSQL streaming read replica:

Terminal window
helm upgrade --install jitsudo ./helm/jitsudo \
--namespace jitsudo \
--create-namespace \
-f helm/jitsudo/values-ha.yaml \
--set config.auth.oidcIssuer=https://your-idp.example.com \
--set config.auth.clientId=jitsudo-server

For full production deployments, combine with an external managed database and your own values file:

Terminal window
helm upgrade --install jitsudo ./helm/jitsudo \
--namespace jitsudo \
-f helm/jitsudo/values-ha.yaml \
--values values-prod.yaml

See the High Availability & DR guide for managed PostgreSQL options, connection pooling, and failure mode analysis.

The config block maps directly to the jitsudod configuration file. See the Server Configuration reference for all options.

ValueDefaultDescription
config.auth.oidcIssuer""OIDC issuer URL (required)
config.auth.clientIdjitsudo-serverOIDC client ID
config.database.url""Inline PostgreSQL URL (use existingSecret in production)
config.database.existingSecret""Name of Secret with DATABASE_URL key
config.tls.enabledfalseEnable TLS for gRPC
config.tls.secretName""Kubernetes TLS secret name
config.log.levelinfoLog level: debug, info, warn, error

Each provider is disabled by default. Enable with config.providers.<name>.enabled: true.

config:
providers:
aws:
enabled: true
region: "us-east-1"
roleArnTemplate: "arn:aws:iam::{scope}:role/jitsudo-{role}"
gcp:
enabled: true
organizationId: "123456789012"
credentialsSource: "workload_identity_federation"
azure:
enabled: true
tenantId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
clientId: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
credentialsSource: "workload_identity"
kubernetes:
enabled: true
defaultNamespace: "default"
maxDuration: "1h"
config:
notifications:
slack:
enabled: true
existingSecret: "jitsudo-slack" # Secret with JITSUDOD_SLACK_WEBHOOK_URL
channel: "#sre-access-requests"
mentionOnBreakGlass: "<!channel>"
smtp:
enabled: true
host: "smtp.example.com"
port: 587
username: "jitsudo@example.com"
existingSecret: "jitsudo-smtp" # Secret with JITSUDOD_SMTP_PASSWORD
from: "jitsudo@example.com"
to:
- "sre-team@example.com"
webhooks:
- url: "https://hooks.example.com/jitsudo"
existingSecret: "jitsudo-webhook" # Secret with url / secret key

If you enable the Kubernetes provider, the chart creates a ClusterRole and ClusterRoleBinding granting the jitsudo ServiceAccount permission to manage RBAC bindings:

# Automatically created by the chart when kubernetes provider is enabled
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterrolebindings", "rolebindings"]
verbs: ["create", "get", "delete", "list"]

Workload Identity (AWS IRSA / GCP Workload Identity)

Section titled “Workload Identity (AWS IRSA / GCP Workload Identity)”

For AWS, annotate the ServiceAccount with the IAM role ARN:

serviceAccount:
annotations:
eks.amazonaws.com/role-arn: "arn:aws:iam::123456789012:role/jitsudo-control-plane"

For GCP, annotate with the service account email:

serviceAccount:
annotations:
iam.gke.io/gcp-service-account: "jitsudo@my-project.iam.gserviceaccount.com"

After installing the chart, run jitsudod init to generate a starter config file. Pass --skip-migrations since the Helm chart runs migrations on startup:

Terminal window
kubectl exec -n jitsudo deployment/jitsudo -- \
jitsudod init \
--db-url "$DATABASE_URL" \
--oidc-issuer https://your-idp.example.com \
--oidc-client-id jitsudo-server \
--skip-migrations
Terminal window
# Check pod status
kubectl get pods -n jitsudo
# Check health endpoints
kubectl port-forward -n jitsudo svc/jitsudo-http 8080:8080
curl http://localhost:8080/healthz # → ok
curl http://localhost:8080/readyz # → ok
curl http://localhost:8080/version # → {"version":"0.1.0","api_versions":["v1alpha1"]}