>_devkit
zitadel-production
skills/zitadel-production/

references/sources.md

Sources and further reading

Cross-checked against the official documentation and the upstream repository. Where guidance conflicted, cmd/defaults.yaml in the source tree won, since it is the only description of the configuration that cannot drift from the binary.

Everything in the deployment, token-settings, audience and mobile sections was verified against a live v4.16.0 instance behind Dokploy and Traefik, including a complete Authorization Code + PKCE exchange, JWKS verification and introspection. Measured figures are from that instance, not estimates.

Configuration

Reverse proxy

Concepts and structure

OIDC and integration

Service accounts, federation and actions

Read for untested.md. Documentation only, not verified against a running instance, unlike everything else cited here.

Operations

Provisioning

Upstream issues referenced

  • #12065 - HTTP used for the discovery document despite ExternalSecure=true, behind Traefik. Open.
  • #4475 - the older Traefik variant of the same report.
  • #9912 - environment.json listing the internal IP rather than the external domain. Fixed.
  • #9109 - PostgreSQL read replica support. Open, and the reason Postgres scales vertically only.

Which claims here are opinion

Worth arguing with, because they are judgement rather than documentation.

  • One instance per client. Defensible for an agency holding other people's user

data. The wrong call if you are building a platform product where clients are tenants you serve, in which case one instance with organizations as customers is correct. That is a business model decision wearing an architecture costume.

  • Validate audience against the project ID. Pragmatic and future-proof, but it

is a deliberate choice to make every application in a project accept every other's tokens. If your applications genuinely need separate trust domains, the right fix is separate projects, not a narrower audience check.

  • BFF over tokens in the browser. Strongly held and occasionally overkill.

Public-client PKCE remains legitimate, and for a statically hosted frontend on a different registrable domain it is more honest than fighting third-party cookie restrictions.

  • Access token lifetime of 5 to 15 minutes. A rule of thumb. The right number

depends on how bad a stale token is for your domain and how well your clients handle refresh. The defensible part is that 12 hours, the default, is wrong for almost everyone.

  • Never TTL-cache introspection. Follows from treating a short-lived JWT as the

cache. Someone running an extremely high-volume sensitive endpoint against a constrained Postgres might reasonably disagree; they should probably fix the endpoint instead.

  • ZITADEL over an in-app library like Better Auth. This follows from needing

both TypeScript and Python. For a purely TypeScript shop, Better Auth's organization plugin covers most of what the reusable module would have been, at zero operational cost, and the recommendation flips.

  • Organizations in the application database for anything that is not B2B SaaS.

Simpler and transactional, but it gives up per-tenant SSO and branding, and retrofitting means mirroring into ZITADEL anyway.

  • JIT provisioning over webhook sync. Robust, but a user does not exist in your

database until they first authenticate, which complicates admin flows that want to reference someone before their first login.

  • "You will never need to scale this." True for the traffic profile of typical

agency client work. Wrong the moment one client's consumer app succeeds, and the read-replica limitation means the ceiling arrives sooner than a stateless binary suggests.

  • Terraform rather than a community CLI. Conservative. The community CLIs are

fine for exploration; the objection is specifically about unaffiliated tooling in a client-facing pipeline.