ZITADEL Docs
Deploy & OperateSelf-HostedManageConfiguration

Adopt Login V2 on an existing installation

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 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.

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

Enabling the instance-level Login V2 feature (required=true) sends every application, including the management console, to Login V2. Before enabling it, make sure a Login V2 UI that can complete the console's OIDC login (for example zitadel-login) is reachable at /ui/v2/login, or at the base URI you configure.

If the login UI is not reachable, console login fails. An existing console session can hide the problem until it expires.

Before enabling the feature, create a break-glass machine user with IAM_OWNER and a PAT, and store it securely. If interactive login breaks, use it to disable the instance feature.

1. Deploy and route

  1. Deploy zitadel-login (compose, Helm, or your own packaging).
  2. On your reverse proxy / ingress, route by path on the same public origin as the API (recommended):
    • /ui/v2/login and its subpaths → login service
    • everything else → ZITADEL API

Starting the login container on its own port is not enough. It must either be reachable under /ui/v2/login on your ZITADEL domain, or you must set a base URI pointing to where it is publicly reachable (see Cut over). If you host the login UI on a separate hostname, also add it to your trusted domains.

See 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.

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 (Upgrade From V9 to V10) for chart-specific details.

Manual equivalent: register a System API user 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

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.

As long as the instance Login V2 feature stays disabled, the console and all other applications keep using Login V1. Enabling Login V2 in the instance settings switches every application, including the console, to Login V2 (see Instance-wide).

This is also how you use Login V2 auth requests with your own login UI (Session API and POST /v2/oidc/auth_requests/{auth_request_id}) without deploying zitadel-login: enable Use new login UI on your applications and set their custom base URL to your UI.

Instance-wide

  1. Verify that the login UI is reachable through your public routing (see Deploy and route).
  2. Enable the loginV2 feature (Console → instance Features, or Feature API with required=true). If the login UI is not served at /ui/v2/login, set the base URI in the same step; otherwise leave it empty.

When the instance feature is on, it takes precedence over per-app settings and applies to every application, including the management console. Per-app custom base URLs are ignored as well: all applications use the instance base URI, or /ui/v2/login if none is set.

The console cannot be exempted from the instance feature. To keep the console on Login V1, leave the instance feature disabled and enable Login V2 per application instead.

Roll back

Disable the per-app setting or the instance feature while Login V1 is still available. Disabling an application's setting has no effect while the instance feature is enabled.

If you are locked out of the console, either restore the login UI and its routing, or use the break-glass IAM_OWNER PAT to disable the instance feature:

curl -X PUT "https://<your-domain>/v2/features/instance" \
  -H "Authorization: Bearer <break-glass-pat>" \
  -H "Content-Type: application/json" \
  -d '{"loginV2": {"required": false}}'

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

Was this page helpful?

On this page