Load Balancing Example
With this example configuration, you create a near production environment for ZITADEL with Docker Compose.
The stack consists of three long-running containers:
- A Traefik reverse proxy with upstream HTTP/2 enabled, issuing a self-signed TLS certificate.
 - A secure ZITADEL container configured for a custom domain. As we terminate TLS with Traefik, we configure ZITADEL for 
--tlsMode external. - A secure CockroachDB.
 
The setup is tested against Docker version 20.10.17 and Docker Compose version v2.2.3
By executing the commands below, you will download the following files:
docker-compose.yaml
version: '3.8'
services:
  traefik:
    networks:
      - 'zitadel'
    image: "traefik:v2.10.1"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./example-traefik.yaml:/etc/traefik/traefik.yaml"
  zitadel:
    restart: 'always'
    networks:
      - 'zitadel'
    image: 'ghcr.io/zitadel/zitadel:stable'
    command: 'start-from-init --config /example-zitadel-config.yaml --config /example-zitadel-secrets.yaml --steps /example-zitadel-init-steps.yaml --masterkey "${ZITADEL_MASTERKEY}" --tlsMode external'
    depends_on:
      certs:
        condition: 'service_completed_successfully'
    volumes:
      - './example-zitadel-config.yaml:/example-zitadel-config.yaml:ro'
      - './example-zitadel-secrets.yaml:/example-zitadel-secrets.yaml:ro'
      - './example-zitadel-init-steps.yaml:/example-zitadel-init-steps.yaml:ro'
      - 'zitadel-certs:/crdb-certs:ro'
  certs:
    image: 'cockroachdb/cockroach:v22.2.2'
    entrypoint: [ '/bin/bash', '-c' ]
    command: [ 'cp /certs/* /zitadel-certs/ && cockroach cert create-client --overwrite --certs-dir /zitadel-certs/ --ca-key /zitadel-certs/ca.key zitadel_user && chown 1000:1000 /zitadel-certs/*' ]
    volumes:
      - 'certs:/certs:ro'
      - 'zitadel-certs:/zitadel-certs:rw'
    depends_on:
      my-cockroach-db:
        condition: 'service_healthy'
  my-cockroach-db:
    restart: 'always'
    networks:
      - 'zitadel'
    image: 'cockroachdb/cockroach:v22.2.2'
    command: 'start-single-node --advertise-addr my-cockroach-db'
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health?ready=1"]
      interval: '10s'
      timeout: '30s'
      retries: 5
      start_period: '20s'
    ports:
      - '9090:8080'
      - '26257:26257'
    volumes:
      - 'certs:/cockroach/certs:rw'
      - 'data:/cockroach/cockroach-data:rw'
networks:
  zitadel:
volumes:
  certs:
  zitadel-certs:
  data:
example-traefik.yaml
log:
  level: DEBUG
accessLog: {}
entrypoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
tls:
  stores:
    default:
      # generates self-signed certificates
      defaultCertificate:
providers:
  file:
    filename: /etc/traefik/traefik.yaml
http:
  middlewares:
    zitadel:
      headers:
        isDevelopment: false
        allowedHosts:
        - 'my.domain'
        customRequestHeaders:
          :authority: 'my.domain'
    redirect-to-https:
      redirectScheme:
        scheme: https
        port: 443
        permanent: true
  routers:
    # Redirect HTTP to HTTPS
    router0:
      entryPoints:
      - web
      middlewares:
      - redirect-to-https
      rule: 'HostRegexp(`my.domain`, `{subdomain:[a-z]+}.my.domain`)'
      service: zitadel
    # The actual ZITADEL router
    router1:
      entryPoints:
      - websecure
      service: zitadel
      middlewares:
      - zitadel
      rule: 'HostRegexp(`my.domain`, `{subdomain:[a-z]+}.my.domain`)'
      tls:
        domains:
          - main: "my.domain"
            sans:
              - "*.my.domain"
              - "my.domain"
  # Add the service
  services:
    zitadel:
      loadBalancer:
        servers:
          # h2c is the scheme for unencrypted HTTP/2
        - url: h2c://zitadel:8080
        passHostHeader: true
example-zitadel-config.yaml
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/defaults.yaml
Log:
  Level: 'info'
# Make ZITADEL accessible over HTTP, not HTTPS
ExternalSecure: true
ExternalDomain: my.domain
ExternalPort: 443
# If not using the docker compose example, adjust these values for connecting ZITADEL to your CockroachDB
Database:
  cockroach:
    Host: 'my-cockroach-db'
    User:
      SSL:
        Mode: 'verify-full'
        RootCert: "/crdb-certs/ca.crt"
        Cert: "/crdb-certs/client.zitadel_user.crt"
        Key: "/crdb-certs/client.zitadel_user.key"
    Admin:
      SSL:
        Mode: 'verify-full'
        RootCert: "/crdb-certs/ca.crt"
        Cert: "/crdb-certs/client.root.crt"
        Key: "/crdb-certs/client.root.key"
LogStore:
  Access:
    Stdout:
      Enabled: true
example-zitadel-secrets.yaml
# 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 CockroachDB
Database:
  cockroach:
    User:
      # If the user doesn't exist already, it is created
      Username: 'zitadel_user'
    Admin:
      Username: 'root'
example-zitadel-init-steps.yaml
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/setup/steps.yaml
FirstInstance:
  Org:
    Name: 'My Org'
    Human:
      # use the loginname root@my-org.my.domain
      Username: 'root'
      Password: 'RootPassword1!'
# Download the docker compose example configuration.
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/loadbalancing-example/docker-compose.yaml
# Download the Traefik example configuration.
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/loadbalancing-example/example-traefik.yaml
# Download and adjust the example configuration file containing standard configuration.
wget https://raw.githubusercontent.com/zitadel/zitadel/main/docs/docs/self-hosting/deploy/loadbalancing-example/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/loadbalancing-example/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/loadbalancing-example/example-zitadel-init-steps.yaml
# A single ZITADEL instance always needs the same 32 characters long masterkey
# If you haven't done so already, you can generate a new one.
# For example:
export ZITADEL_MASTERKEY="$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32)"
# Run the database and application containers
docker compose up --detach
Make 127.0.0.1 available at my.domain. For example, this can be achived with an entry 127.0.1.1 my.domain in the /etc/hosts file.
Open your favorite internet browser at https://my.domain/ui/console/. You can safely proceed, if your browser warns you about the insecure self-signed TLS certificate. This is the IAM admin users login according to your configuration in the example-zitadel-init-steps.yaml:
- username: root@my-org.my.domain
 - password: RootPassword1!
 
Read more about the login process.
Troubleshooting
You can connect to cockroach like this: docker exec -it loadbalancing-example-my-cockroach-db-1 cockroach sql --host my-cockroach-db --certs-dir /cockroach/certs/
For example, to show all login names: docker exec -it loadbalancing-example-my-cockroach-db-1 cockroach sql --database zitadel --host my-cockroach-db --certs-dir /cockroach/certs/ --execute "select * from projections.login_names2"