Context: this is part of Guides — Deploy. Start with the primer if you haven't.
Three ways to run authserver on Kubernetes. None are mutually exclusive — many teams run kind locally, then Helm in staging/prod.
- Decide between Helm, raw manifests, and kind for your situation.
- Know which page to read next.
- Kubernetes 1.26+ for any production target.
- A TLS termination story (Ingress controller, service mesh, or LoadBalancer with cert-manager).
- Read Configuration — you'll be setting the same knobs regardless of path.
| Path | Best for | What you get | Read next |
|---|---|---|---|
| Helm | Production deployments | Postgres subchart, init-container wait, Vault Transit, HPA, PDB, NetworkPolicy, ServiceMonitor, dual ingresses for public/admin | Helm |
| Raw manifests | GitOps pipelines that don't use Helm (Argo, Flux + Kustomize, Jsonnet) | Copy-paste YAML, no chart dependency, full control over every object | Raw manifests |
| kind | Local end-to-end testing | Run the Helm chart on your laptop; reproduce OIDC + MCP Inspector flows | kind local testing |
- Production → Helm. The chart already encodes the answers to "do you want NetworkPolicy?", "how do I keep the admin port internal?", and "how do I pre-create the secrets so
helm upgradedoesn't rotate them?". Start here. - GitOps without Helm → start from the raw manifests, wrap in Kustomize / Jsonnet, commit the rendered YAML.
- Pre-prod validation → use kind to reproduce the production Helm chart locally before each release.
- Admin port stays internal. The admin surface (
:9001) hosts the Admin API + UI +/metrics. Never expose it via the public Ingress. Use a separateadminIngresswith an IP allowlist (Helm) orClusterIP+kubectl port-forward(raw). - Signing keys across replicas. A single PVC with
ReadWriteOnceonly works when all replicas land on one node. For real multi-node, usesigning.key_store: postgres_keyorvault_transit. - Graceful shutdown. Set
terminationGracePeriodSeconds≥server.shutdown_wait+ LB drain time. See systemd → SIGTERM. - Purge as a CronJob.
authserver purgeis not automatic. Deploy the CronJob in Backup & purge → Kubernetes CronJob.
# Same probes regardless of how you deployed
kubectl -n <ns> port-forward svc/authplane 9000:9000 &
curl -fsS http://localhost:9000/.well-known/oauth-authorization-server | jq -r .issuer
curl -fsS http://localhost:9000/health | jq .| Symptom | Likely cause | Fix |
|---|---|---|
| Two replicas, signing keys diverge | keyfile store with ReadWriteOnce PVC; pods on different nodes |
Switch to postgres_key or vault_transit. |
| Admin endpoints reachable from internet | Admin path mounted on the public Ingress | Move to a separate hostname + IP-allowlist; or keep ClusterIP only. |
| In-flight requests aborted on rolling update | terminationGracePeriodSeconds < shutdown_wait |
Raise grace period; default is 30 s. |
| Browser-based MCP clients fail CORS preflight against the public Ingress | server.allowed_origins empty (boot logs WARN) |
Set AUTHPLANE_SERVER_ALLOWED_ORIGINS on the Deployment / values. |
| Tables grow unbounded over weeks | No authserver purge CronJob wired |
Deploy from Backup & purge → Kubernetes CronJob. |
- Helm — recommended production deploy.
- Raw manifests — Helm-free path.
- kind local testing — laptop end-to-end.
charts/authplane/values.yaml— every Helm value.