Skip to content

Kubernetes Deployment

The Helm chart is the production-shaped Kubernetes deployment. It wires Postgres, secret management, an init-container DB wait, Vault Transit, ingress with separate paths for the public and admin surfaces, HPA, PDB, NetworkPolicy, and ServiceMonitor. Prereqs: Kubernetes 1.26+, Helm 3.8+, a reachable Postgres (managed service, in-cluster, or the chart’s Bitnami subchart for dev), and TLS termination (cert-manager + ingress-nginx is the assumed default).

  1. Install the chart.

    From the OCI registry:

    Terminal window
    helm install authplane oci://ghcr.io/authplane/charts/authplane \
    --version 0.2.0 -f values-production.yaml

    Or from source while iterating:

    Terminal window
    git clone https://github.com/authplane/authserver.git
    cd authserver
    helm dependency update charts/authplane
    helm install authplane charts/authplane -f values-production.yaml --create-namespace -n authplane
  2. Pre-create the secrets.

    Auto-generated secrets.* values rotate on every helm upgrade — never use them outside dev. Mint once and reference:

    Terminal window
    kubectl create secret generic authplane-secrets \
    --from-literal=session-secret=$(openssl rand -hex 32) \
    --from-literal=admin-api-key=$(openssl rand -hex 32)
    kubectl create secret generic authplane-db \
    --from-literal=dsn="postgres://authplane:$PASS@postgres.prod.svc:5432/authplane?sslmode=require"

    Both keys are validated at boot — an admin-api-key under 16 chars or in the default blocklist (changeme, dev-admin-key, …) is rejected.

  3. Write the production values.

    values-production.yaml
    replicaCount: 3
    image:
    repository: authplane/authserver
    tag: "" # defaults to .Chart.AppVersion; pin for repeatability
    config:
    server:
    issuer: https://auth.example.com # AUTHPLANE_SERVER_ISSUER
    shutdown_wait: 20s
    allowed_origins: # required for browser-based MCP clients
    - https://app.example.com
    storage:
    driver: postgres # AUTHPLANE_STORAGE_DRIVER
    postgres:
    max_conns: 25
    signing:
    algorithm: ES256
    key_store: postgres_key # multi-replica safe; needs data_encryption
    data_encryption:
    driver: aes_master
    session:
    secure: true
    same_site: lax
    admin:
    address: ":9001"
    client_credentials:
    enabled: true # most MCP integrations need this
    observability:
    logging: { level: info, format: json }
    metrics: { provider: prometheus }
    externalDatabase:
    host: postgres.prod.svc
    port: 5432
    database: authplane
    sslmode: require
    existingSecret: authplane-db
    existingSecretKey: dsn # full DSN in one secret key
    secrets:
    existingSecret: authplane-secrets # contains session-secret + admin-api-key
    resources:
    requests: { cpu: 100m, memory: 128Mi }
    limits: { cpu: 1, memory: 512Mi }
    autoscaling:
    enabled: true
    minReplicas: 3
    maxReplicas: 8
    targetCPUUtilizationPercentage: 70
    podDisruptionBudget:
    enabled: true
    minAvailable: 2
    networkPolicy:
    enabled: true
    serviceMonitor:
    enabled: true
    interval: 15s
    ingress:
    enabled: true
    className: nginx
    annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    hosts:
    - host: auth.example.com
    paths: [{ path: /, pathType: Prefix }]
    tls:
    - secretName: auth-tls
    hosts: [auth.example.com]
    adminIngress:
    enabled: true
    className: nginx
    annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/whitelist-source-range: 10.0.0.0/8
    hosts:
    - host: auth-admin.internal.example.com
    paths: [{ path: /, pathType: Prefix }]
    tls:
    - secretName: auth-admin-tls
    hosts: [auth-admin.internal.example.com]
  4. Install and verify.

    Terminal window
    kubectl -n authplane rollout status deploy/authplane --timeout=120s
    curl -fsS https://auth.example.com/.well-known/oauth-authorization-server | jq -r .issuer
    curl -fsS https://auth.example.com/health | jq .
    ADMIN_KEY=$(kubectl -n authplane get secret authplane-secrets -o jsonpath='{.data.admin-api-key}' | base64 -d)
    curl -fsS -H "Authorization: Bearer $ADMIN_KEY" \
    https://auth-admin.internal.example.com/admin/stats | jq .

    Migrations run automatically inside the main container (authserver serve migrates before binding); the chart’s init container only waits for Postgres to be reachable.

The auth server is stateless when storage.driver: postgres and signing.key_store is postgres_key or vault_transit — scale horizontally with the HPA:

  • Signing keys across replicas. A keyfile store on a ReadWriteOnce PVC only works when all replicas land on one node. With postgres_key, keys live in Postgres (AES-encrypted via data_encryption), and a rotation — kubectl exec deploy/authplane -- authserver admin key rotate — propagates to all replicas via Postgres LISTEN/NOTIFY within milliseconds, zero downtime.
  • No session affinity needed. Sessions are signed cookies, not server-side state.
  • Connection budget. Total open connections = storage.postgres.max_conns × replicas; managed Postgres plans (RDS, Cloud SQL) cap at a few hundred — tune accordingly.
  • Graceful shutdown. Keep terminationGracePeriodSeconds (cluster default 30s) ≥ config.server.shutdown_wait + LB drain time, or the kubelet SIGKILLs pods mid-drain.
  • PDB. podDisruptionBudget.minAvailable: 2 keeps the OAuth surface up through node drains.

Upgrade with helm upgrade authplane charts/authplane -f values-production.yaml --atomic --timeout 5m — migrations apply on pod boot, and --atomic rolls back on failure. Schedule authserver purge as a CronJob — it is not automatic (recipe).

For GitOps pipelines that don’t use Helm (Argo CD + Kustomize, Flux + Jsonnet), the repo documents copy-paste manifests that mirror what the chart renders: a Deployment, dual Services (public 9000, admin 9001 as ClusterIP only), a public-only Ingress, and a PDB — paired with signing.key_store: postgres_key for multi-replica. See Kubernetes — raw manifests and the Helm guide for Vault Transit signing and OIDC federation values.

Next: Configuration · Observability