From 8395d4326e39a631c367fe43ce59bcff350c2dcc Mon Sep 17 00:00:00 2001 From: Livio Spring <9405495+livio-a@users.noreply.github.com> Date: Wed, 15 Jul 2026 17:14:18 +0200 Subject: [PATCH] docs: add v3 to v4 upgrade and Login V2 adoption guides (#12443) # Which Problems Are Solved - Operators upgrading from ZITADEL v3 to v4 lacked a clear, docs-backed upgrade path covering web keys (A-10017), Login V1 vs Login V2 expectations, and post-upgrade options. - There was no standalone guide for adopting Login V2 after already running on v4. - Related ops/docs pages did not consistently point readers to the upgrade and Login V2 adoption material. - Kubernetes/Helm operators upgrading to a chart that ships ZITADEL v4 had no ops-page section linking the advisory, upgrade guide, optional Login V2 deferral (`login.enabled: false`), and chart README upgrade notes. # How the Problems Are Solved - Adds technical advisory **A-10017** documenting OIDC web key staging requirements before upgrading to v4. - Adds an **upgrade-v3-to-v4** guide with the recommended upgrade path (Login V1 remains supported; Login V2 is optional at upgrade time). - Adds a **v4-only adopt-login-v2** guide for teams adopting Login V2 later, with steps and caveats separate from the version upgrade. - Adds an **Upgrading to ZITADEL v4** subsection under `self-hosting/deploy/kubernetes/operations.mdx` that links to A-10017, the upgrade guide, Adopt Login V2, notes `login.enabled: false` when not adopting Login V2 yet, and points to the [Helm chart README](https://github.com/zitadel/zitadel-charts/blob/main/charts/zitadel/README.md) for chart-specific breaking changes. # Additional Changes - Registers new docs in the sidebar and wires Related / cross-links from upgrade and ops pages so the advisory and guides are discoverable together. - Aligns language so upgrade vs. Login V2 adoption are clearly separated concerns. - Light cross-link updates on troubleshooting / updating-scaling / login-client pages where related. # Additional Context - Related: A-10017 (web keys advisory) - Docs paths introduced/updated: - `apps/docs/content/support/advisory/a10017.mdx` - `apps/docs/content/self-hosting/manage/upgrade-v3-to-v4.mdx` - `apps/docs/content/self-hosting/manage/adopt-login-v2.mdx` - `apps/docs/content/self-hosting/deploy/kubernetes/operations.mdx` (new "Upgrading to ZITADEL v4" section) - Sidebar and Related / cross-links - Companion Helm chart README PR: https://github.com/zitadel/zitadel-charts/pull/608 --------- Co-authored-by: Cursor --- .../deploy/kubernetes/operations.mdx | 8 ++ .../troubleshooting/troubleshooting.mdx | 2 +- .../self-hosting/manage/adopt-login-v2.mdx | 124 ++++++++++++++++++ .../self-hosting/manage/login-client.mdx | 2 + .../self-hosting/manage/updating_scaling.mdx | 2 + .../self-hosting/manage/upgrade-v3-to-v4.mdx | 54 ++++++++ apps/docs/content/support/advisory/a10017.mdx | 44 +++++++ .../content/support/technical_advisory.mdx | 13 ++ apps/docs/lib/sidebar-data.ts | 2 + 9 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 apps/docs/content/self-hosting/manage/adopt-login-v2.mdx create mode 100644 apps/docs/content/self-hosting/manage/upgrade-v3-to-v4.mdx create mode 100644 apps/docs/content/support/advisory/a10017.mdx diff --git a/apps/docs/content/self-hosting/deploy/kubernetes/operations.mdx b/apps/docs/content/self-hosting/deploy/kubernetes/operations.mdx index 3205b9c14..2ae77c30b 100644 --- a/apps/docs/content/self-hosting/deploy/kubernetes/operations.mdx +++ b/apps/docs/content/self-hosting/deploy/kubernetes/operations.mdx @@ -34,6 +34,14 @@ Check the Helm release status: helm status my-zitadel ``` +### Upgrading to ZITADEL v4 + +If you are moving from ZITADEL v3 to v4 (for example via a chart version that ships ZITADEL v4), follow [Upgrade from ZITADEL v3 to v4](/self-hosting/manage/upgrade-v3-to-v4) and technical advisory [A-10017](/support/advisory/a10017) for the recommended OIDC web key staging path. + +Newer charts may deploy a Login V2 workload by default. That does **not** enable Login V2 for existing instances. If you are not adopting Login V2 yet, set `login.enabled: false`. When you are ready to cut over, see [Adopt Login V2 on an existing installation](/self-hosting/manage/adopt-login-v2). + +For chart-specific breaking changes (for example chart v8 → v9 or v9 → v10), always check the [Helm chart README](https://github.com/zitadel/zitadel-charts/blob/main/charts/zitadel/README.md) upgrade notes as well. + ## Scaling ### Manual Scaling diff --git a/apps/docs/content/self-hosting/deploy/troubleshooting/troubleshooting.mdx b/apps/docs/content/self-hosting/deploy/troubleshooting/troubleshooting.mdx index e9c6a302e..3a8bb7380 100644 --- a/apps/docs/content/self-hosting/deploy/troubleshooting/troubleshooting.mdx +++ b/apps/docs/content/self-hosting/deploy/troubleshooting/troubleshooting.mdx @@ -40,7 +40,7 @@ To change instance-level settings on a running installation: - Use the Admin API to automate changes. **Login V2**: Starting with ZITADEL v4, the Login V2 UI is enabled by default for new installations. -If you are running an existing installation and want to enable it, follow the [Login Client guide](/self-hosting/manage/login-client). +Existing instances keep Login V1 until you opt in. To adopt Login V2 on an existing v4 installation, see [Adopt Login V2](/self-hosting/manage/adopt-login-v2). ## ZITADEL service becomes unhealthy without visible error messages diff --git a/apps/docs/content/self-hosting/manage/adopt-login-v2.mdx b/apps/docs/content/self-hosting/manage/adopt-login-v2.mdx new file mode 100644 index 000000000..8ba17bfd8 --- /dev/null +++ b/apps/docs/content/self-hosting/manage/adopt-login-v2.mdx @@ -0,0 +1,124 @@ +--- +title: Adopt Login V2 on an existing installation +description: "Deploy and enable Hosted Login V2 for self-hosted ZITADEL v4 instances that currently use Login V1" +sidebar_label: Adopt Login V2 +--- + +## Who this is for + +Self-hosted **ZITADEL v4** deployments with existing instances that still use **Login V1**. + +Login V2 was not the default on v3 and is not a supported cutover target there. If you are still on v3, [upgrade to v4](/self-hosting/manage/upgrade-v3-to-v4) first, then return here. + +New v4 installations may enable Login V2 by default. That does **not** rewrite existing instances. + +## Do you need to switch? + +**No — not for normal v4 operation.** Remaining on Login V1 is supported and is the right default unless you have a concrete reason to move. + +Consider Login V2 when you need capabilities that depend on it (including many **Actions V2** flows). Plan extra time if you rely on **Actions V1** or deep Login V1 customizations: those usually must be migrated, and some setups need careful validation because not every V1 pattern has an identical V2 equivalent yet. +See [Migrate from Actions V1 to V2](/guides/integrate/actions/migrate-from-v1). + +If you do not urgently need Login V2, stay on Login V1 and revisit later. + +## Overview + +1. Deploy and route the Login UI (no production traffic yet) +2. Authenticate login → API (PAT **or** system-user JWT / X.509) +3. Smoke-test Login V2 +4. Cut over gradually (per app) or instance-wide +5. Update IdP callbacks and related config + + +Before enabling Login V2 for production traffic, create a break-glass machine user with `IAM_OWNER` and a PAT. +If login misconfiguration locks interactive access, use the PAT to revert the feature or fix routing. + + +## 1. Deploy and route + +1. Deploy `zitadel-login` (compose, Helm, or your own packaging). +2. On your reverse proxy / ingress: + - `/ui/v2/login` → login service + - everything else → ZITADEL API + +See [Login UI routing](/self-hosting/manage/reverseproxy/reverse_proxy#login-ui-routing). + +## 2. Authenticate login to the API + +Pick **one** method. Login resolves credentials roughly as: + +1. System-user JWT (`AUDIENCE`, `SYSTEM_USER_ID`, `SYSTEM_USER_PRIVATE_KEY` / `_FILE`) +2. `ZITADEL_LOGINCLIENT_KEYFILE` (subject `login-client`) +3. Service-user PAT (`ZITADEL_SERVICE_USER_TOKEN` / `_FILE`) + +### Option A — PAT (classic) + +For **existing** instances the setup job does **not** auto-create the login client. + +1. Create a machine user +2. Grant **Instance Login Client** (`IAM_LOGIN_CLIENT`) +3. Create a PAT and provide it to the login UI + +Step-by-step: [Create a Login Client](/self-hosting/manage/login-client). + +### Option B — RSA / X.509 system user (recommended on recent Helm) + +On **Helm chart ≥ v10** with **ZITADEL ≥ v4.14**: + +- The chart generates an RSA keypair (or uses `login.loginServiceKeySecretName`) +- Registers the public cert under `SystemAPIUsers` (login client) +- Mounts the private key into the login container + +No login PAT is required for login↔API auth. +Older `login-client` PAT secrets from chart v9 are unused after that switch and can be deleted. + +See the [Helm chart README](https://github.com/zitadel/zitadel-charts/blob/main/charts/zitadel/README.md) (Upgrade From V9 to V10) for chart-specific details. + +Manual equivalent: register a [System API user](/guides/integrate/zitadel-apis/access-zitadel-system-api) with `IAM_LOGIN_CLIENT` membership and configure the login env vars above. + +## 3. Smoke-test (feature still off) + +Open `https:///ui/v2/login` and verify health while production apps still use Login V1. + +Also prepare: + +- External IdP redirect / callback URLs for Login V2 paths +- Trusted domains if login is hosted on a separate hostname + +## 4. Cut over + +### Gradual (recommended) + +With the instance Login V2 feature **disabled**: + +1. Open the application in Console +2. Enable **Use new login UI** +3. Optionally set a custom base URL for a separately hosted login UI + +Only that application uses Login V2. See [Application settings](/guides/manage/console/applications-overview#use-new-login-ui). + +### Instance-wide + +1. Enable the **loginV2** feature (Console → instance Features, or Feature API) +2. Set the base URI, or leave empty to use `/ui/v2/login` + +When the instance feature is on, it takes precedence over per-app checkboxes and applies to every application. + +### Roll back + +Disable the per-app setting or the instance feature while Login V1 is still available. +Use the break-glass `IAM_OWNER` PAT if interactive login is broken. + +## 5. After cutover checklist + +- IdP callbacks updated and tested +- SAML / attribute mapping: review Actions V2 needs for Login V2 where applicable +- Login V2 texts: customize via Settings API (not the old Console login text editor) +- OIDC apps that already use your ZITADEL issuer usually need **no** code changes + +## Related + +- [Hosted Login](/guides/integrate/login/hosted-login) +- [Create a Login Client](/self-hosting/manage/login-client) +- [Access System API](/guides/integrate/zitadel-apis/access-zitadel-system-api) +- [Upgrade from ZITADEL v3 to v4](/self-hosting/manage/upgrade-v3-to-v4) diff --git a/apps/docs/content/self-hosting/manage/login-client.mdx b/apps/docs/content/self-hosting/manage/login-client.mdx index e4b2d53f2..cc0970c0b 100644 --- a/apps/docs/content/self-hosting/manage/login-client.mdx +++ b/apps/docs/content/self-hosting/manage/login-client.mdx @@ -11,6 +11,8 @@ Check out [one of the deployment examples](https://zitadel.com/docs/self-hosting However, if you want to replace the v1 login of an existing installation by a self-hosted v2 login, the setup job won't execute these steps. In that case, you can create a new PAT for the login client manually. +For the full cutover on an existing **v4** installation (routing, auth options, gradual vs instance-wide enablement), see [Adopt Login V2 on an existing installation](/self-hosting/manage/adopt-login-v2). + ## Create a Login Client User In the following URLs, replace the base URL and the user ID according to your environment. diff --git a/apps/docs/content/self-hosting/manage/updating_scaling.mdx b/apps/docs/content/self-hosting/manage/updating_scaling.mdx index bb5d5f9ae..70ab7b40c 100644 --- a/apps/docs/content/self-hosting/manage/updating_scaling.mdx +++ b/apps/docs/content/self-hosting/manage/updating_scaling.mdx @@ -40,6 +40,8 @@ Also, after the setup is done, rolling out a new ZITADEL version is much faster when the runtime processes are just executed with `zitadel start`. +If you are upgrading from **v3 to v4**, see [Upgrade from ZITADEL v3 to v4](/self-hosting/manage/upgrade-v3-to-v4) and technical advisory [A-10017](/support/advisory/a10017) for the recommended staging path around OIDC web keys. + ## Separating Init and Setup from the Runtime If you use the [official ZITADEL Helm chart](/self-hosting/deploy/kubernetes), diff --git a/apps/docs/content/self-hosting/manage/upgrade-v3-to-v4.mdx b/apps/docs/content/self-hosting/manage/upgrade-v3-to-v4.mdx new file mode 100644 index 000000000..851587783 --- /dev/null +++ b/apps/docs/content/self-hosting/manage/upgrade-v3-to-v4.mdx @@ -0,0 +1,54 @@ +--- +title: Upgrade from ZITADEL v3 to v4 +description: "Recommended path for upgrading self-hosted ZITADEL from v3 to v4" +sidebar_label: Upgrade v3 → v4 +--- + +## Summary + +Upgrading from v3 to v4 is a normal ZITADEL upgrade: run **setup** (migrations), then **start**. +Existing instances keep working with their current login setup. Switching to Login V2 is optional and can be done later. + +The main behavioral risk on this upgrade is OIDC signing keys. Follow [A-10017](/support/advisory/a10017) before moving to v4. + +## Prerequisites + +- Database backup +- PostgreSQL version compatible with your target v4 release ([database requirements](/self-hosting/manage/database)) + +## Recommended path + +1. Upgrade to the latest **v3.4.x** (at least **v3.4.1**). +2. Soak until legacy-signed JWTs/sessions have expired (see [A-10017](/support/advisory/a10017)). +3. Upgrade to **v4.x**: + - Existing installs: prefer `zitadel setup` then `zitadel start`, or `start-from-setup` + - Details: [Update and Scale](/self-hosting/manage/updating_scaling) + + +`ZITADEL_FIRSTINSTANCE_*` and `ZITADEL_DEFAULTINSTANCE_*` apply only when an instance is created. +Changing them and restarting does not update existing instances. + + +## After the upgrade + +OIDC applications that already use your ZITADEL issuer usually need no changes. + +**Staying on Login V1 is fine.** Existing instances keep Login V1, and you do not need to switch for a successful v4 upgrade. +Adopt Login V2 only if you specifically need it (for example features or flows that depend on Login V2 / Actions V2). +A switch can involve more than deploying the new UI — especially if you use **Actions V1**, which typically need to be reworked for **Actions V2**, and not every Login V1 customization maps one-to-one. + +When you decide to move, see [Adopt Login V2 on an existing installation](/self-hosting/manage/adopt-login-v2). + +## Helm + +Follow the [Helm chart README](https://github.com/zitadel/zitadel-charts/blob/main/charts/zitadel/README.md) upgrade notes for the ZITADEL app version (for example chart v8 → v9 when moving to ZITADEL v4). + +Newer charts may deploy a Login V2 workload by default. That does **not** enable Login V2 for existing instances. +If you are not adopting Login V2 yet, set `login.enabled: false` to omit the login Deployment. + +## Related + +- [A-10017: Web keys and v4 upgrades](/support/advisory/a10017) +- [Update and Scale](/self-hosting/manage/updating_scaling) +- [Adopt Login V2](/self-hosting/manage/adopt-login-v2) +- [Troubleshoot](/self-hosting/deploy/troubleshooting/troubleshooting) diff --git a/apps/docs/content/support/advisory/a10017.mdx b/apps/docs/content/support/advisory/a10017.mdx new file mode 100644 index 000000000..683edd6f8 --- /dev/null +++ b/apps/docs/content/support/advisory/a10017.mdx @@ -0,0 +1,44 @@ +--- +title: Technical Advisory 10017 +description: "A-10017: Upgrading to ZITADEL v4 without previously enabling OIDC web keys can invalidate existing JWTs" +--- + +## Date and Version + +Versions: upgrades to >= v4.0.0 from installations that never activated OIDC web keys + +Date: 2026-07-15 + +## Description + +ZITADEL signs OIDC ID tokens (and JWT access tokens, when configured) with asymmetric keys exposed on the JWKS endpoint. + +On v3, instances could still use **legacy signing keys** until the `web_key` feature was enabled. +Starting with v4, **web keys are mandatory** and the legacy signing path is removed. +Setup generates web keys if they are missing, but tokens that were signed with legacy keys before the upgrade are no longer verifiable. + +To prepare existing deployments, v3.4 introduced web key setup support, and **v3.4.1** enables the web key feature by default so new tokens can roll onto web keys while legacy keys may remain on JWKS until they expire. + +## Impact + +If you upgrade to v4 without having web keys active beforehand: + +- Existing JWT access tokens and ID tokens may become invalid immediately +- Users may be forced to re-authenticate +- Flows that rely on `id_token_hint` (for example Console logout / re-login) can fail until session storage is cleared + +## Mitigation + +1. Upgrade to the latest **v3.4.x** (at least **v3.4.1**). +2. Let tokens and sessions signed with legacy keys expire (soak), **or** accept a planned re-login window. +3. Upgrade to v4 using your normal process (`setup`, then `start`). See [Upgrade from ZITADEL v3 to v4](/self-hosting/manage/upgrade-v3-to-v4). + +### No action needed if + +- You already run **>= v3.4.1** and have soaked since enabling web keys, or +- You previously enabled the `web_key` feature on v3 and waited for legacy-signed tokens to expire + +## Related + +- [OIDC web keys](/guides/integrate/login/oidc/webkeys) +- [Upgrade from ZITADEL v3 to v4](/self-hosting/manage/upgrade-v3-to-v4) diff --git a/apps/docs/content/support/technical_advisory.mdx b/apps/docs/content/support/technical_advisory.mdx index d7514eaa8..7e10682d8 100644 --- a/apps/docs/content/support/technical_advisory.mdx +++ b/apps/docs/content/support/technical_advisory.mdx @@ -251,6 +251,19 @@ We understand that these advisories may include breaking changes, and we aim to 2.65.10 2025-05-14 + + + A-10017 + + OIDC web keys required for v4 upgrades + Breaking Behavior Change + + Upgrading to v4 without previously enabling OIDC web keys can invalidate + existing JWTs. Stage through v3.4.1+ and soak before moving to v4. + + >= v4.0.0 + 2026-07-15 + ## Subscribe to our Mailing List diff --git a/apps/docs/lib/sidebar-data.ts b/apps/docs/lib/sidebar-data.ts index a81866604..51ac763e4 100644 --- a/apps/docs/lib/sidebar-data.ts +++ b/apps/docs/lib/sidebar-data.ts @@ -744,6 +744,7 @@ export const guidesSidebar: readonly SidebarItem[] = [ "self-hosting/manage/tls_modes", "self-hosting/manage/http2", "self-hosting/manage/login-client", + "self-hosting/manage/adopt-login-v2", ], }, { @@ -809,6 +810,7 @@ export const guidesSidebar: readonly SidebarItem[] = [ label: "Scaling & Performance", items: [ "self-hosting/manage/updating_scaling", + "self-hosting/manage/upgrade-v3-to-v4", "self-hosting/manage/database/database", "self-hosting/manage/cache", ],