From 7e184727b0fea17c9c4285646c078ea7b88d95ce Mon Sep 17 00:00:00 2001 From: Elio Bischof Date: Wed, 6 Aug 2025 22:48:54 +0200 Subject: [PATCH] docs: simplify compose example (#10407) # Which Problems Are Solved Using the compose configuration with for existing installation doesn't work. It is not appreciated in the whole community, that we use Traefik for the deployment example. # How the Problems Are Solved - The config we used before v4 is reused and extended in a compatible way. - Traefik is removed, Zitadel is accessible again at localhost:8080 and the login at localhost:3000. Deploying with Traefik is described already here http://localhost:3000/docs/self-hosting/manage/reverseproxy/traefik - A page is added that describes how to create a login client and switch from login v1 to v2. The page is linked in a hint in the compose example. # Additional Changes - The config also sets up a machine user and documents it inline. This allows us to avoid using the cumbersome separate docker-compose-sa.yaml # Additional Context - Closes #10379 - Internal discussion https://zitadel.slack.com/archives/C08TL9AURL7/p1754471184222879 - Discord about upgrading to v2 login: - https://discord.com/channels/927474939156643850/927866013545025566/threads/1401950163940933804 - https://discord.com/channels/927474939156643850/927866013545025566/1401312126030708756 - Discord about not found errors: - https://discord.com/channels/927474939156643850/927866013545025566/threads/1401173877941473291 - https://discord.com/channels/927474939156643850/927866013545025566/1401045717849604227 - https://discord.com/channels/927474939156643850/927866013545025566/1401173877941473291 - https://discord.com/channels/927474939156643850/927866013545025566/1401301168998584361 --- docs/docs/self-hosting/deploy/.gitignore | 2 +- .../docs/self-hosting/deploy/_defaultuser.mdx | 10 +- docs/docs/self-hosting/deploy/compose.mdx | 60 +++---- .../self-hosting/deploy/docker-compose.yaml | 167 ++++++++---------- .../deploy/example-zitadel-config.yaml | 22 --- .../deploy/example-zitadel-init-steps.yaml | 11 -- .../deploy/example-zitadel-secrets.yaml | 12 -- .../docs/self-hosting/manage/login-client.mdx | 47 +++++ docs/sidebars.js | 1 + 9 files changed, 149 insertions(+), 183 deletions(-) delete mode 100644 docs/docs/self-hosting/deploy/example-zitadel-config.yaml delete mode 100644 docs/docs/self-hosting/deploy/example-zitadel-init-steps.yaml delete mode 100644 docs/docs/self-hosting/deploy/example-zitadel-secrets.yaml create mode 100644 docs/docs/self-hosting/manage/login-client.mdx diff --git a/docs/docs/self-hosting/deploy/.gitignore b/docs/docs/self-hosting/deploy/.gitignore index 83754bbee4..aba9338c1f 100644 --- a/docs/docs/self-hosting/deploy/.gitignore +++ b/docs/docs/self-hosting/deploy/.gitignore @@ -1 +1 @@ -login-client-pat +*.pat \ No newline at end of file diff --git a/docs/docs/self-hosting/deploy/_defaultuser.mdx b/docs/docs/self-hosting/deploy/_defaultuser.mdx index aee45b83c0..885b5f9a3e 100644 --- a/docs/docs/self-hosting/deploy/_defaultuser.mdx +++ b/docs/docs/self-hosting/deploy/_defaultuser.mdx @@ -1,8 +1,6 @@ -Open your favorite internet browser and navigate to [http://localhost:8080/ui/console](http://localhost:8080/ui/console). -This is the default IAM admin users login: -- **username**: *zitadel-admin@zitadel.localhost* -- **password**: *Password1!* +Open your favorite internet browser and navigate to http://localhost:8080/ui/console?login_hint=zitadel-admin@zitadel.localhost. +Enther the password *Password1!* to log in. :::info - -In the above username, replace localhost with your configured external domain, if any. e.g. with *zitadel-admin@zitadel.sso.my.domain.tld* +In the above login hint in the URL, replace localhost with your configured external domain, if any. e.g. with *zitadel-admin@zitadel.sso.my.domain.tld* +::: \ No newline at end of file diff --git a/docs/docs/self-hosting/deploy/compose.mdx b/docs/docs/self-hosting/deploy/compose.mdx index f47a33da16..8496a9b53f 100644 --- a/docs/docs/self-hosting/deploy/compose.mdx +++ b/docs/docs/self-hosting/deploy/compose.mdx @@ -5,61 +5,45 @@ sidebar_label: Docker Compose import CodeBlock from '@theme/CodeBlock'; import DockerComposeSource from '!!raw-loader!./docker-compose.yaml' -import ExampleZitadelConfigSource from '!!raw-loader!./example-zitadel-config.yaml' -import ExampleZitadelSecretsSource from '!!raw-loader!./example-zitadel-secrets.yaml' -import ExampleZitadelInitStepsSource from '!!raw-loader!./example-zitadel-init-steps.yaml' +import Disclaimer from './_disclaimer.mdx' +import DefaultUser from './_defaultuser.mdx' +import Next from './_next.mdx' +import NoteInstanceNotFound from './troubleshooting/_note_instance_not_found.mdx'; -The stack consists of four long-running containers and a couple of short-lived containers: -- A [Traefik](https://doc.traefik.io/traefik/) reverse proxy container with upstream HTTP/2 enabled, issuing a self-signed TLS certificate. -- A Login container that is accessible via Traefik at `/ui/v2/login` -- A Zitadel container that is accessible via Traefik at all other paths than `/ui/v2/login`. -- An insecure [PostgreSQL](https://www.postgresql.org/docs/current/index.html). - -The Traefik container and the login container call the Zitadel container via the internal Docker network at `h2c://zitadel:8080` The setup is tested against Docker version 28.3.2 and Docker Compose version v2.38.2 -By executing the commands below, you will download the following files: +## Docker compose + +By executing the commands below, you will download the following file:
docker-compose.yaml {DockerComposeSource}
-
- example-zitadel-config.yaml - {ExampleZitadelConfigSource} -
-
- example-zitadel-secrets.yaml - {ExampleZitadelSecretsSource} -
-
- example-zitadel-init-steps.yaml - {ExampleZitadelInitStepsSource} -
```bash # Download the docker compose example configuration. wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/docker-compose.yaml -# Download and adjust the example configuration file containing standard configuration. -wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/example-zitadel-config.yaml - -# Download and adjust the example configuration file containing secret configuration. -wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/example-zitadel-secrets.yaml - -# Download and adjust the example configuration file containing database initialization configuration. -wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/example-zitadel-init-steps.yaml - -# Make sure you have the latest version of the images +# Make sure you have the latest image versions docker compose pull -# Run the containers +# Run the PostgreSQL database, the Zitadel API and the Zitadel login. docker compose up ``` -Open your favorite internet browser at https://localhost/ui/console?login_hint=zitadel-admin@zitadel.localhost. -Your browser warns you about the insecure self-signed TLS certificate. As localhost resolves to your local machine, you can safely proceed. -Use the password *Password1!* to log in. + -Read more about [the login process](/guides/integrate/login/oidc/login-users). \ No newline at end of file +:::info +If you ran these commands for an existing instance that still uses the login v1, [create a login client for it to the now running v2 login](/self-hosting/manage/login-client#create-login-client). +Move the login client PAT to `./login-client.pat` and restart the login container. +```bash +docker compose restart login +``` +Now, [enable the Login UI for all users](/self-hosting/manage/login-client#require-login-v2) +::: + + + + diff --git a/docs/docs/self-hosting/deploy/docker-compose.yaml b/docs/docs/self-hosting/deploy/docker-compose.yaml index 23d651efc9..ba27bfb645 100644 --- a/docs/docs/self-hosting/deploy/docker-compose.yaml +++ b/docs/docs/self-hosting/deploy/docker-compose.yaml @@ -1,117 +1,98 @@ services: - - db: - image: postgres:17-alpine + zitadel: restart: unless-stopped + image: ghcr.io/zitadel/zitadel:latest + command: start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled environment: - - POSTGRES_USER=root - - POSTGRES_PASSWORD=postgres - networks: - - 'storage' + ZITADEL_EXTERNALSECURE: false + ZITADEL_TLS_ENABLED: false + ZITADEL_DATABASE_POSTGRES_HOST: db + ZITADEL_DATABASE_POSTGRES_PORT: 5432 + ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel + ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel + ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel + ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable + ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: postgres + ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: postgres + ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable + # By configuring a login client, the setup job creates a user of type machine with the role IAM_LOGIN_CLIENT. + # It writes a PAT to the path specified in ZITADEL_FIRSTINSTANCE_LOGINCLIENTPATPATH. + # The PAT is passed to the login container via the environment variable ZITADEL_SERVICE_USER_TOKEN_FILE. + ZITADEL_FIRSTINSTANCE_LOGINCLIENTPATPATH: /current-dir/login-client.pat + ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: false + ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_USERNAME: login-client + ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_MACHINE_NAME: Automatically Initialized IAM_LOGIN_CLIENT + ZITADEL_FIRSTINSTANCE_ORG_LOGINCLIENT_PAT_EXPIRATIONDATE: '2029-01-01T00:00:00Z' + ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: true + ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI: http://localhost:3000/ui/v2/login + ZITADEL_OIDC_DEFAULTLOGINURLV2: http://localhost:3000/ui/v2/login/login?authRequest= + ZITADEL_OIDC_DEFAULTLOGOUTURLV2: http://localhost:3000/ui/v2/login/logout?post_logout_redirect= + ZITADEL_SAML_DEFAULTLOGINURLV2: http://localhost:3000/ui/v2/login/login?samlRequest= + # By configuring a machine, the setup job creates a user of type machine with the role IAM_OWNER. + # It writes a personal access token (PAT) to the path specified in ZITADEL_FIRSTINSTANCE_PATPATH. + # The PAT can be used to provision resources with [Terraform](/docs/guides/manage/terraform-provider), for example. + ZITADEL_FIRSTINSTANCE_PATPATH: /current-dir/admin.pat + ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_USERNAME: admin + ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINE_NAME: Automatically Initialized IAM_OWNER + ZITADEL_FIRSTINSTANCE_ORG_MACHINE_MACHINEKEY_TYPE: 1 + healthcheck: - test: [ "CMD-SHELL", "pg_isready", "-d", "db_prod" ] + test: + - CMD + - /app/zitadel + - ready interval: 10s timeout: 60s retries: 5 start_period: 10s volumes: - - 'data:/var/lib/postgresql/data:rw' - - zitadel-init: - restart: 'no' + - .:/current-dir:delegated + ports: + - 8080:8080 + - 3000:3000 networks: - - 'storage' - image: 'ghcr.io/zitadel/zitadel:v4.0.0-rc.2' - command: [ init, --config, /example-zitadel-config.yaml, --config, /example-zitadel-secrets.yaml ] + - zitadel depends_on: db: - condition: 'service_healthy' - volumes: - - './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro' - - './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro' - - zitadel-setup: - restart: 'no' - networks: - - 'storage' - image: 'ghcr.io/zitadel/zitadel:v4.0.0-rc.2' - command: [ setup, --config, /current-dir/example-zitadel-config.yaml, --config, /current-dir/example-zitadel-secrets.yaml, --steps, /current-dir/example-zitadel-init-steps.yaml, --masterkey, MasterkeyNeedsToHave32Characters ] - depends_on: - zitadel-init: - condition: 'service_completed_successfully' - restart: false - volumes: - - '.:/current-dir:rw' - - zitadel: - restart: 'unless-stopped' - networks: - - 'backend' - - 'storage' - labels: - - "traefik.http.routers.zitadel.rule=!PathPrefix(`/ui/v2/login`)" - - "traefik.http.routers.zitadel.tls=true" # Traefik uses a self-signed certificate - - "traefik.http.services.zitadel.loadbalancer.passhostheader=true" - - "traefik.http.services.zitadel.loadbalancer.server.scheme=h2c" - - "traefik.http.services.zitadel.loadbalancer.server.port=8080" - image: 'ghcr.io/zitadel/zitadel:v4.0.0-rc.2' - command: [ start, --config, /example-zitadel-config.yaml, --config, /example-zitadel-secrets.yaml, --masterkey, MasterkeyNeedsToHave32Characters ] - depends_on: - zitadel-setup: - condition: 'service_completed_successfully' - restart: true - volumes: - - './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro' - - './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro' - healthcheck: - test: [ "CMD", "/app/zitadel", "ready", "--config", "/example-zitadel-config.yaml", "--config", "/example-zitadel-secrets.yaml" ] - interval: 10s - timeout: 60s - retries: 5 - start_period: 10s + condition: service_healthy login: - restart: 'unless-stopped' - labels: - - "traefik.http.routers.login.rule=PathPrefix(`/ui/v2/login`)" - - "traefik.http.routers.login.tls=true" # Traefik uses a self-signed certificate - - "traefik.http.services.login.loadbalancer.passhostheader=true" - - "traefik.http.services.login.loadbalancer.server.port=3000" - image: 'ghcr.io/zitadel/zitadel-login:v4.0.0-rc.2' + restart: unless-stopped + image: ghcr.io/zitadel/zitadel-login:latest # If you can't use the network_mode service:zitadel, you can pass the environment variable CUSTOM_REQUEST_HEADERS=Host:localhost instead. - network_mode: service:zitadel environment: - ZITADEL_API_URL=http://localhost:8080 - NEXT_PUBLIC_BASE_PATH=/ui/v2/login - - ZITADEL_SERVICE_USER_TOKEN_FILE=/current-dir/login-client-pat + - ZITADEL_SERVICE_USER_TOKEN_FILE=/current-dir/login-client.pat user: "${UID:-1000}" + network_mode: service:zitadel volumes: - - '.:/current-dir:ro' - depends_on: - zitadel-setup: - condition: 'service_completed_successfully' - restart: false - - traefik: - image: traefik:latest - command: --providers.docker --api.insecure=true --entrypoints.websecure.address=:443 --log.level=DEBUG --accesslog - networks: - - 'backend' - ports: - - "443:443" - - "8080:8080" - volumes: - - /var/run/docker.sock:/var/run/docker.sock + - .:/current-dir:ro depends_on: zitadel: - condition: 'service_healthy' - login: - condition: 'service_started' + condition: service_healthy + restart: false + + db: + restart: unless-stopped + image: postgres:17-alpine + environment: + PGUSER: postgres + POSTGRES_PASSWORD: postgres + healthcheck: + test: + - CMD-SHELL + - pg_isready + - -d + - zitadel + - -U + - postgres + interval: 10s + timeout: 30s + retries: 5 + start_period: 20s + networks: + - zitadel networks: - storage: - backend: - - -volumes: - data: + zitadel: diff --git a/docs/docs/self-hosting/deploy/example-zitadel-config.yaml b/docs/docs/self-hosting/deploy/example-zitadel-config.yaml deleted file mode 100644 index baacd6cefe..0000000000 --- a/docs/docs/self-hosting/deploy/example-zitadel-config.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/defaults.yaml - -ExternalSecure: true -ExternalPort: 443 - -# Traefik terminates TLS. Inside the Docker network, we use plain text. -TLS.Enabled: false - -# If not using the docker compose example, adjust these values for connecting ZITADEL to your PostgreSQL -Database: - postgres: - Host: 'db' - Port: 5432 - Database: zitadel - User.SSL.Mode: 'disable' - Admin.SSL.Mode: 'disable' - -# Access logs allow us to debug Network issues -LogStore.Access.Stdout.Enabled: true - -# Skipping the MFA init step allows us to immediately authenticate at the console -DefaultInstance.LoginPolicy.MfaInitSkipLifetime: "0s" diff --git a/docs/docs/self-hosting/deploy/example-zitadel-init-steps.yaml b/docs/docs/self-hosting/deploy/example-zitadel-init-steps.yaml deleted file mode 100644 index 373c6ae744..0000000000 --- a/docs/docs/self-hosting/deploy/example-zitadel-init-steps.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/setup/steps.yaml -FirstInstance: - LoginClientPatPath: '/current-dir/login-client-pat' - Org: - # We want to authenticate immediately at the console without changing the password - Human.PasswordChangeRequired: false - LoginClient: - Machine: - Username: 'login-client' - Name: 'Automatically Initialized IAM Login Client' - Pat.ExpirationDate: '2029-01-01T00:00:00Z' \ No newline at end of file diff --git a/docs/docs/self-hosting/deploy/example-zitadel-secrets.yaml b/docs/docs/self-hosting/deploy/example-zitadel-secrets.yaml deleted file mode 100644 index 242da43b24..0000000000 --- a/docs/docs/self-hosting/deploy/example-zitadel-secrets.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/defaults.yaml - -# If not using the docker compose example, adjust these values for connecting ZITADEL to your PostgreSQL -Database: - postgres: - User: - # If the user doesn't exist already, it is created - Username: 'zitadel_user' - Password: 'zitadel' - Admin: - Username: 'root' - Password: 'postgres' diff --git a/docs/docs/self-hosting/manage/login-client.mdx b/docs/docs/self-hosting/manage/login-client.mdx new file mode 100644 index 0000000000..977fa4b8c6 --- /dev/null +++ b/docs/docs/self-hosting/manage/login-client.mdx @@ -0,0 +1,47 @@ +--- +title: Connect your Self-Hosted Login UI to Zitadel +sidebar_label: Create a Login Client +--- + +To enable your self-hosted Login UI to connect to the Zitadel API, it needs a token for a user with the IAM_LOGIN_CLIENT role. +On new installations, the Zitadel setup job can be configured to automatically write a Personal Access Token (PAT) for the login client. +Check out [one of the deployment examples](https://zitadel.com/docs/self-hosting/deploy/overview) to learn how to do this. + +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. + +## Create a Login Client User{#create-login-client} + +In the following URLs, replace the base URL and the user ID according to your environment. + +1. Create a new machine user, for example at http://localhost:8080/ui/console/users/create-machine +2. Create a PAT, for example at http://localhost:8080/ui/console/users/332169800719532035?new=true&id=pat +3. Save the PAT to a file, for example `/path/on/your/host/login-client.pat` +4. Make sure the user has the `Iam Login Client` role (internally called `IAM_LOGIN_CLIENT`), for example at http://localhost:8080/ui/console/instance/members + +# Configure the Login UI + +Make sure your Login UI has the environment variable `ZITADEL_SERVICE_USER_TOKEN` set with your PAT. +If you run the Login UI with Docker, you can also mount the file into the container and reference it by passing the environment variable `ZITADEL_SERVICE_USER_TOKEN_FILE`. +For example: + +```bash +docker run -p 3000:3000 -v /path/on/your/host/login-client.pat:/path/in/container/login-client.pat:ro -e ZITADEL_SERVICE_USER_TOKEN_FILE=/path/in/container/login-client.pat ghcr.io/zitadel/zitadel-login:latest +``` + +# Enable the Login UI for all users{#require-login-v2} + +:::caution +Before doing this, make sure you have a working PAT for an Iam Owner user. +In case something goes wrong and you lock yourself out from the login screen, you can revert the changes. +Create a machine user PAT like you created the [login client PAT above](#create-login-client), but give the user the Iam Owner role (internally called `IAM_OWNER`). +::: + +Enable the `Login V2` feature flag, for example at the bottom of http://localhost:8080/ui/console/instance?id=features. +Enter the base URI of your Login UI, for example `http://localhost:3000/ui/v2/login`. + +# Test + +That's it! +Click your users avatar in the top right corner of the console and select `Log in With Another Account`. +You should see the new Login UI. diff --git a/docs/sidebars.js b/docs/sidebars.js index acca7b1659..aa11908a44 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -1094,6 +1094,7 @@ module.exports = { items: [ "self-hosting/manage/production", "self-hosting/manage/productionchecklist", + "self-hosting/manage/login-client", "self-hosting/manage/configure/configure", { type: "category",