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).
-
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.yamlOr from source while iterating:
Terminal window git clone https://github.com/authplane/authserver.gitcd authserverhelm dependency update charts/authplanehelm install authplane charts/authplane -f values-production.yaml --create-namespace -n authplane -
Pre-create the secrets.
Auto-generated
secrets.*values rotate on everyhelm 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-keyunder 16 chars or in the default blocklist (changeme,dev-admin-key, …) is rejected. -
Write the production values.
values-production.yaml replicaCount: 3image:repository: authplane/authservertag: "" # defaults to .Chart.AppVersion; pin for repeatabilityconfig:server:issuer: https://auth.example.com # AUTHPLANE_SERVER_ISSUERshutdown_wait: 20sallowed_origins: # required for browser-based MCP clients- https://app.example.comstorage:driver: postgres # AUTHPLANE_STORAGE_DRIVERpostgres:max_conns: 25signing:algorithm: ES256key_store: postgres_key # multi-replica safe; needs data_encryptiondata_encryption:driver: aes_mastersession:secure: truesame_site: laxadmin:address: ":9001"client_credentials:enabled: true # most MCP integrations need thisobservability:logging: { level: info, format: json }metrics: { provider: prometheus }externalDatabase:host: postgres.prod.svcport: 5432database: authplanesslmode: requireexistingSecret: authplane-dbexistingSecretKey: dsn # full DSN in one secret keysecrets:existingSecret: authplane-secrets # contains session-secret + admin-api-keyresources:requests: { cpu: 100m, memory: 128Mi }limits: { cpu: 1, memory: 512Mi }autoscaling:enabled: trueminReplicas: 3maxReplicas: 8targetCPUUtilizationPercentage: 70podDisruptionBudget:enabled: trueminAvailable: 2networkPolicy:enabled: trueserviceMonitor:enabled: trueinterval: 15singress:enabled: trueclassName: nginxannotations:cert-manager.io/cluster-issuer: letsencrypt-prodhosts:- host: auth.example.compaths: [{ path: /, pathType: Prefix }]tls:- secretName: auth-tlshosts: [auth.example.com]adminIngress:enabled: trueclassName: nginxannotations:cert-manager.io/cluster-issuer: letsencrypt-prodnginx.ingress.kubernetes.io/whitelist-source-range: 10.0.0.0/8hosts:- host: auth-admin.internal.example.compaths: [{ path: /, pathType: Prefix }]tls:- secretName: auth-admin-tlshosts: [auth-admin.internal.example.com] -
Install and verify.
Terminal window kubectl -n authplane rollout status deploy/authplane --timeout=120scurl -fsS https://auth.example.com/.well-known/oauth-authorization-server | jq -r .issuercurl -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 servemigrates before binding); the chart’s init container only waits for Postgres to be reachable.
Multi-instance HA
Section titled “Multi-instance HA”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
keyfilestore on aReadWriteOncePVC only works when all replicas land on one node. Withpostgres_key, keys live in Postgres (AES-encrypted viadata_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: 2keeps 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).
Raw manifests (Helm-free)
Section titled “Raw manifests (Helm-free)”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