Skip to main content
Ory Kratos can be deployed to Kubernetes using Helm charts or raw Kubernetes manifests.

Helm chart

The recommended way to deploy Kratos on Kubernetes is using the official Helm chart:
1

Add the Ory Helm repository

helm repo add ory https://k8s.ory.sh/helm/charts
helm repo update
2

Install Kratos

helm install kratos ory/kratos \
  --namespace kratos \
  --create-namespace
3

Verify the installation

kubectl get pods -n kratos
kubectl get svc -n kratos

Helm values configuration

Create a values.yaml file to customize your deployment:
kratos:
  config:
    dsn: postgres://user:password@postgres:5432/kratos?sslmode=require
    
    serve:
      public:
        base_url: https://auth.example.com
        cors:
          enabled: true
          allowed_origins:
            - https://example.com
      admin:
        base_url: https://admin-auth.example.com
    
    selfservice:
      default_browser_return_url: https://example.com/
      allowed_return_urls:
        - https://example.com
    
    identity:
      default_schema_id: default
      schemas:
        - id: default
          url: base64://ewogICIkaWQiOiAiaHR0cHM6Ly9zY2hlbWFzLm9yeS5zaC9wcmVzZXRzL2tyYXRvcy9pZGVudGl0eS5lbWFpbC5zY2hlbWEuanNvbiIsCiAgInRpdGxlIjogIlBlcnNvbiIsCiAgInR5cGUiOiAib2JqZWN0IiwKICAicHJvcGVydGllcyI6IHsKICAgICJ0cmFpdHMiOiB7CiAgICAgICJ0eXBlIjogIm9iamVjdCIsCiAgICAgICJwcm9wZXJ0aWVzIjogewogICAgICAgICJlbWFpbCI6IHsKICAgICAgICAgICJ0eXBlIjogInN0cmluZyIsCiAgICAgICAgICAiZm9ybWF0IjogImVtYWlsIiwKICAgICAgICAgICJvcnkuc2gva3JhdG9zIjogewogICAgICAgICAgICAiY3JlZGVudGlhbHMiOiB7CiAgICAgICAgICAgICAgInBhc3N3b3JkIjogewogICAgICAgICAgICAgICAgImlkZW50aWZpZXIiOiB0cnVlCiAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAidmVyaWZpY2F0aW9uIjogewogICAgICAgICAgICAgICJ2aWEiOiAiZW1haWwiCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJyZWNvdmVyeSI6IHsKICAgICAgICAgICAgICAidmlhIjogImVtYWlsIgogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9LAogICAgICAicmVxdWlyZWQiOiBbImVtYWlsIl0KICAgIH0KICB9Cn0=

replicaCount: 2

image:
  repository: oryd/kratos
  tag: v25.4.0
  pullPolicy: IfNotPresent

service:
  public:
    enabled: true
    type: ClusterIP
    port: 80
  admin:
    enabled: true
    type: ClusterIP
    port: 80

ingress:
  public:
    enabled: true
    className: nginx
    hosts:
      - host: auth.example.com
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: auth-tls
        hosts:
          - auth.example.com
  
  admin:
    enabled: true
    className: nginx
    hosts:
      - host: admin-auth.example.com
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: admin-auth-tls
        hosts:
          - admin-auth.example.com

resources:
  limits:
    cpu: 500m
    memory: 512Mi
  requests:
    cpu: 100m
    memory: 128Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 80

job:
  automigrate:
    enabled: true
Install with your custom values:
helm install kratos ory/kratos \
  -f values.yaml \
  --namespace kratos \
  --create-namespace

Raw Kubernetes manifests

If you prefer not to use Helm, here’s a basic Kubernetes deployment:

Namespace

apiVersion: v1
kind: Namespace
metadata:
  name: kratos

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: kratos-config
  namespace: kratos
data:
  kratos.yml: |
    version: v0.13.0
    dsn: postgres://kratos:secret@postgresql:5432/kratos?sslmode=require
    
    serve:
      public:
        base_url: https://auth.example.com/
        cors:
          enabled: true
      admin:
        base_url: https://admin.example.com/
    
    selfservice:
      default_browser_return_url: https://example.com/
      flows:
        login:
          ui_url: https://example.com/auth/login
        registration:
          ui_url: https://example.com/auth/registration

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kratos
  namespace: kratos
spec:
  replicas: 2
  selector:
    matchLabels:
      app: kratos
  template:
    metadata:
      labels:
        app: kratos
    spec:
      containers:
      - name: kratos
        image: oryd/kratos:v25.4.0
        command: ["kratos"]
        args: ["serve", "-c", "/etc/config/kratos.yml"]
        ports:
        - name: public
          containerPort: 4433
        - name: admin
          containerPort: 4434
        volumeMounts:
        - name: config
          mountPath: /etc/config
        env:
        - name: DSN
          valueFrom:
            secretKeyRef:
              name: kratos-secrets
              key: dsn
        livenessProbe:
          httpGet:
            path: /health/alive
            port: admin
          initialDelaySeconds: 5
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health/ready
            port: admin
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: config
        configMap:
          name: kratos-config

Services

apiVersion: v1
kind: Service
metadata:
  name: kratos-public
  namespace: kratos
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 4433
    name: public
  selector:
    app: kratos
---
apiVersion: v1
kind: Service
metadata:
  name: kratos-admin
  namespace: kratos
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 4434
    name: admin
  selector:
    app: kratos

Database migration job

Run database migrations before starting Kratos:
apiVersion: batch/v1
kind: Job
metadata:
  name: kratos-migrate
  namespace: kratos
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: migrate
        image: oryd/kratos:v25.4.0
        command: ["kratos"]
        args: ["-c", "/etc/config/kratos.yml", "migrate", "sql", "-e", "--yes"]
        volumeMounts:
        - name: config
          mountPath: /etc/config
        env:
        - name: DSN
          valueFrom:
            secretKeyRef:
              name: kratos-secrets
              key: dsn
      volumes:
      - name: config
        configMap:
          name: kratos-config

Secrets management

Store sensitive data in Kubernetes Secrets:
apiVersion: v1
kind: Secret
metadata:
  name: kratos-secrets
  namespace: kratos
type: Opaque
stringData:
  dsn: postgres://kratos:secret@postgresql:5432/kratos?sslmode=require
  secrets-default: "your-32-character-secret-here"
  secrets-cookie: "your-32-character-cookie-secret"
  secrets-cipher: "your-32-character-cipher-secret"
Never commit secrets to version control. Use tools like Sealed Secrets, External Secrets Operator, or Vault for production.

Health checks

Kratos exposes health check endpoints:
  • /health/alive - Liveness probe (checks if Kratos is running)
  • /health/ready - Readiness probe (checks if Kratos can handle requests)

Scaling considerations

Configure HPA based on CPU and memory:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: kratos
  namespace: kratos
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: kratos
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 80
Configure connection limits in your DSN:
postgres://user:pass@host/db?max_conns=20&max_idle_conns=10

Next steps

Database setup

Configure production database

Configuration

Complete configuration reference

Build docs developers (and LLMs) love