docs: improve compose example (#10518)

# Which Problems Are Solved

The [compose deployment
example](https://zitadel.com/docs/self-hosting/deploy/compose) has
improved guidance and a more robust compose configuration. It provides
clear paths to a variety of target environments.

# How the Problems Are Solved

- The introduction sentences make clear that the setup is not intended
to be used as is in production.
- Info blocks are removed as they rather increased the mental overhead
instead of drawing attention to important hints.
- The What's next section adds links and hints that help evolving the
setup towards production.
- The docker-compose.yaml explains variables better, gives hints and
provides configuration examples.
- The root user is used to write and read the `login-client.pat` file to
avoid file permission errors and failing setup steps.
- The postgres data is persisted in a named volume, so it survives the
PostgreSQL container lifecycle.
- `curl` is used instead of `wget`, because `curl` is more likely to
already be installed on hosts.

# Additional Context

- Closes #10432 
- Closes #8910
- Implements changes proposed
[here](https://github.com/zitadel/zitadel/issues/10432#issuecomment-3188569674)
and
[here](https://github.com/zitadel/zitadel/issues/10432#issuecomment-3191360487).

To test the changes, you can't download the compose file as suggested
[by the preview
doc](https://docs-nuzruxtoh-zitadel.vercel.app/docs/self-hosting/deploy/compose).
As the updated compose file is not merged to main yet, you have to use a
different download link:

```shell
curl -L https://raw.githubusercontent.com/zitadel/zitadel/improve-compose-example/docs/docs/self-hosting/deploy/docker-compose.yaml -o docker-compose.yaml
```

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
Elio Bischof
2025-08-21 20:40:05 +02:00
committed by GitHub
parent ef799b9a7e
commit 473c33754f
3 changed files with 116 additions and 43 deletions

View File

@@ -1,6 +1 @@
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 login hint in the URL, replace localhost with your configured external domain, if any. e.g. with *zitadel-admin@<span></span>zitadel.sso.my.domain.tld*
:::
Visit http://localhost:8080/ui/console?login_hint=zitadel-admin@zitadel.localhost and enter `Password1!` to log in.

View File

@@ -10,40 +10,80 @@ import DefaultUser from './_defaultuser.mdx'
import Next from './_next.mdx'
import NoteInstanceNotFound from './troubleshooting/_note_instance_not_found.mdx';
This guide is the entrypoint for running the Zitadel platform locally for the first time.
It is for demonstration and development purposes only and does not set up a production-ready and security hardened instance of Zitadel.
Once Zitadel up is and running, learn more about a production setup in the [What's Next section](#next-steps).
The setup is tested against Docker version 28.3.2 and Docker Compose version v2.38.2
## Prerequisites
The setup is likely to work with other software versions too, but it is tested against the following environment:
- Ubuntu 24.04.1
- Docker 28.3.2
- Docker Compose v2.38.2
- curl 8.5.0
## Docker compose
By executing the commands below, you will download the following file:
The following commands set up services for a PostgreSQL database, a Zitadel API and a Zitadel login:
1. Download the `docker-compose.yaml` file from the Zitadel repository:
```shell
curl -L https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/docker-compose.yaml -o docker-compose.yaml
```
2. Make sure the containers use the latest image versions.
```shell
docker compose pull
```
3. Run the PostgreSQL database, the Zitadel API, and the Zitadel login.
```shell
docker compose up --detach --wait
```
4. Verify the containers are running and healthy.
```shell
docker compose ps
```
<DefaultUser/>
## What's next{#next-steps}
Before proceeding, review the downloaded `docker-compose.yaml` file:
The comments give context to the used variables and show examples for commonly used configuration variants.
<details>
<summary>docker-compose.yaml</summary>
<CodeBlock language="yaml">{DockerComposeSource}</CodeBlock>
</details>
```bash
# Download the docker compose example configuration.
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/docker-compose.yaml
Here are some natural steps forward to go from the current setup to production:
# Make sure you have the latest image versions
docker compose pull
- **Use a different master key**: Set up an instance from scratch and use a different master key. For example, generate one by running `tr -dc A-Za-z0-9 </dev/urandom | head -c 32`
- **Use files for secrets instead of environment variables, and don't commit them to Git**: Learn how to [configure the platform](/docs/self-hosting/manage/configure)
- **Run the containers as non-root users**: Remove the `user: "0"` lines from the `docker-compose.yaml` file.
- **Configure a different external domain or IP**: Beware that the login only works with HTTPS on non-localhost domains. [Run and configure a reverse proxy](/docs/self-hosting/manage/reverseproxy/reverse_proxy)
- **Serve the API and the UI together on the same port**: [Run and configure a reverse proxy](/docs/self-hosting/manage/reverseproxy/reverse_proxy)
- **Encrypt Traffic**: [Run and configure a reverse proxy](/docs/self-hosting/manage/reverseproxy/reverse_proxy)
# Run the PostgreSQL database, the Zitadel API and the Zitadel login.
docker compose up
```
For more detailed recommendations, read the [Zitadel Production Setup Guide](/docs/self-hosting/manage/production).
<DefaultUser components={props.components} />
<details>
<summary>Read this if the environment uses the login v1</summary>
<p>
The login v2 is the next generation of the Zitadel login.
Its code and deployment are easily customizable.
Unlike the login v1, it runs in its own process.
The login v2 is enabled by default in new installations.
But if an existing `docker-compose.yaml` file is updated by following this guide,
an additional `zitadel-login` service is now running which is actually not doing anything, yet.
The following steps give guidance on how to enable the login v2 in an environment.
</p>
<ol>
<li>Read the login v2 related comments in the docker-compose.yaml.</li>
<li><a href="/docs/self-hosting/manage/login-client#create-login-client">Manually create a login client personal access token (PAT) for the now running v2 login</a>.</li>
<li>Move the PAT to `./login-client.pat`</li>
<li>Restart the login process: `docker compose restart login`</li>
<li><a href="/docs/self-hosting/manage/login-client#require-login-v2">Enable the Login UI for all users</a></li>
</ol>
</details>
:::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)
:::
<NoteInstanceNotFound/>
<Next components={props.components} />
<Disclaimer components={props.components} />

View File

@@ -2,19 +2,29 @@ services:
zitadel:
restart: unless-stopped
image: ghcr.io/zitadel/zitadel:latest
command: start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled
command: start-from-init --masterkey "MasterkeyNeedsToHave32Characters"
environment:
# See "What's next" to learn about how to serve Zitadel on a different domain or IP.
ZITADEL_EXTERNALDOMAIN: localhost
# See "What's next" to learn about how to enable TLS.
ZITADEL_EXTERNALSECURE: false
ZITADEL_TLS_ENABLED: false
# Database connection settings.
ZITADEL_DATABASE_POSTGRES_HOST: db
ZITADEL_DATABASE_POSTGRES_PORT: 5432
# The database is created by the init job if it does not exist.
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
# The admin user must already exist in the database.
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME: postgres
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD: postgres
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE: disable
# The zitadel user is created by the init job if it does not exist.
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel
ZITADEL_DATABASE_POSTGRES_USER_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.
@@ -23,28 +33,49 @@ services:
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
# Activate the login v2 on an installation from scratch.
# To activate the login v2 on an existing installation, read the "What's next" section.
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_REQUIRED: true # To use the login v1, set this to false.
ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI: http://localhost:3000/ui/v2/login
# Configure the redirection paths to the login v2.
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
# 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_PAT_EXPIRATIONDATE: '2029-01-01T00:00:00Z'
# To change the initial human admin users username and password, uncomment the following lines.
# The first login name is formatted like this: <username>@<org_name>.<external_domain>
# With the following incommented configuration, this would be root@my-organization.localhost
# Visit http://localhost:8080/ui/console to check if the login name works.
# If you can't log in, check the available login names:
# echo "select * from projections.login_names3;" | psql -h localhost -U postgres -d zitadel
# The postgres users password is postgres.
# ZITADEL_FIRSTINSTANCE_ORG_NAME: My Organization
# ZITADEL_FIRSTINSTANCE_ORG_HUMAN_USERNAME: root
# ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: RootPassword1!
# Enable debug logs
# ZITADEL_LOG_LEVEL: debug
# Write Access Logs to stdout.
# ZITADEL_LOGSTORE_ACCESS_STDOUT_ENABLED: true
healthcheck:
test:
- CMD
- /app/zitadel
- ready
- CMD
- /app/zitadel
- ready
interval: 10s
timeout: 60s
retries: 5
start_period: 10s
user: "0"
volumes:
- .:/current-dir:delegated
ports:
@@ -59,13 +90,13 @@ services:
login:
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.
# If you can't use the network_mode service:zitadel, you can pass the environment variables ZITADEL_API_URL=http://zitadel:8080 and CUSTOM_REQUEST_HEADERS=Host:localhost instead.
environment:
- ZITADEL_API_URL=http://localhost:8080
- NEXT_PUBLIC_BASE_PATH=/ui/v2/login
- ZITADEL_SERVICE_USER_TOKEN_FILE=/current-dir/login-client.pat
user: "${UID:-1000}"
network_mode: service:zitadel
user: "0"
volumes:
- .:/current-dir:ro
depends_on:
@@ -93,6 +124,13 @@ services:
start_period: 20s
networks:
- zitadel
ports:
- 5432:5432
volumes:
- 'data:/var/lib/postgresql/data:rw'
networks:
zitadel:
volumes:
data: