docs: add v3 to v4 upgrade and Login V2 adoption guides (#12443)

<!--
Please inform yourself about the contribution guidelines on submitting a
PR here:
https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr.
Take note of how PR/commit titles should be written and replace the
template texts in the sections below. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->

# 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 <[email protected]>
This commit is contained in:
Livio Spring
2026-07-15 15:14:18 +00:00
committed by GitHub
co-authored by Cursor
parent 93209bd348
commit 8395d4326e
9 changed files with 250 additions and 1 deletions
@@ -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
@@ -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
@@ -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
<Callout type="warn">
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.
</Callout>
## 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://<your-domain>/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)
@@ -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.
@@ -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),
@@ -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)
<Callout type="info">
`ZITADEL_FIRSTINSTANCE_*` and `ZITADEL_DEFAULTINSTANCE_*` apply only when an instance is created.
Changing them and restarting does not update existing instances.
</Callout>
## 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)
@@ -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)
@@ -251,6 +251,19 @@ We understand that these advisories may include breaking changes, and we aim to
<td>2.65.10</td>
<td>2025-05-14</td>
</tr>
<tr>
<td>
<a href="./advisory/a10017">A-10017</a>
</td>
<td>OIDC web keys required for v4 upgrades</td>
<td>Breaking Behavior Change</td>
<td>
Upgrading to v4 without previously enabling OIDC web keys can invalidate
existing JWTs. Stage through v3.4.1+ and soak before moving to v4.
</td>
<td>&gt;= v4.0.0</td>
<td>2026-07-15</td>
</tr>
</table>
## Subscribe to our Mailing List
+2
View File
@@ -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",
],