SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor

Jak zainstalować Zitadel na VPS przy użyciu Docker

Instrukcja samodzielnego hostowania Zitadel na serwerze VPS. Dowiedz się, jak skonfigurować PostgreSQL, masterkey, TLS, SMTP oraz kopie zapasowe przy zalecanych 4 rdzeniach CPU.

Wymagania do samodzielnego hostowania Zitadel na VPS

Aby samodzielnie hostować Zitadel na serwerze VPS, wymagany jest host Docker, publiczna nazwa DNS wskazująca na serwer, baza danych PostgreSQL oraz około 4 rdzenie CPU i 8 GB pamięci RAM. Zitadel jest dostawcą tożsamości. Wydaje tokeny za pośrednictwem OIDC (OpenID Connect) oraz SAML (security assertion markup language), dzięki czemu inne usługi nie muszą utrzymywać własnych list użytkowników. Instalacja składa się z curl oraz docker compose up. Elementy decydujące o stabilności działania to masterkey, użytkownik bazy danych, SMTP (simple mail transfer protocol), kopia zapasowa oraz pierwsza aktualizacja.

Wszystkie poniższe instrukcje zakładają użycie systemu Ubuntu 24.04, Docker Engine w wersji 24 lub nowszej z wtyczką Compose oraz nazwę domenową typu auth.example.com, która już wskazuje na serwer.

Ile zasobów VPS wymaga Zitadel?

Szybki start z użyciem Compose w dokumentacji Zitadel wskazuje na 2 GB pamięci RAM. Ta wartość dotyczy środowiska lokalnego. Przewodnik wdrożeniowy Zitadel dla środowisk produkcyjnych podaje inne liczby.

ChartZitadel's own published sizing guidance, August 2026
The data behind this chart
[
  {
    "config": "Process floor, no load",
    "cpu_cores": 0.5,
    "ram_gb": 0.5
  },
  {
    "config": "Single node, reduced setup",
    "cpu_cores": 4,
    "ram_gb": 8
  },
  {
    "config": "HA node, logs and metrics on",
    "cpu_cores": 4,
    "ram_gb": 16
  }
]

Są to zalecenia, a nie pomiary z działającego serwera. Należy traktować je jako szacunkowe wymagania. Sam proces Zitadel jest lekki i w stanie spoczynku zajmuje około 0.5 GB pamięci RAM. Rdzenie procesora są wykorzystywane do haszowania haseł, co jest procesem celowo spowolnionym, więc nagły wzrost liczby logowań powoduje skokowe obciążenie CPU. Drugim istotnym elementem jest PostgreSQL: ten sam przewodnik zakłada około jednego rdzenia na każde 100 żądań na sekundę oraz 4 GB pamięci RAM na rdzeń. Sumując te wartości, otrzymujemy 4 rdzenie oraz 8 GB pamięci RAM dla pojedynczego węzła, lub 16 GB na węzeł po włączeniu logowania i metryk.

VPS z 2 GB pamięci RAM uruchomi ten stos, jednak jest to wartość poniżej wymagań zalecanych dla środowisk produkcyjnych. Logowanie to usługa, od której zależą wszystkie pozostałe. Gdy jest niedostępna, żadna zintegrowana aplikacja nie pozwoli na dostęp użytkownikom. Decyzja o tym, czy 8 GB pamięci RAM to zbyt wysoki koszt dla systemu uwierzytelniania, jest zasadna, a podjęcie jej przed migracją jest znacznie tańsze. Porównanie Keycloak, Authentik i Zitadel omawia koszty pamięciowe oraz nakład pracy operacyjnej dla każdego z tych rozwiązań, a samodzielnie hostowany serwer Authentik jest zazwyczaj wybierany w przypadku mniejszych maszyn.

Pobranie stosu i przypięcie wersji

mkdir zitadel-compose && cd zitadel-compose
curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/.env.example
cp .env.example .env
chmod 600 .env

Ten plik definiuje cztery usługi, które zostaną uruchomione. Traefik pełni rolę reverse proxy: kieruje ruch według ścieżki, a dzięki poniższej nakładce dokonuje terminacji TLS (transport layer security). zitadel-api to binarny plik Go działający na porcie 8080. zitadel-login to interfejs logowania dostępny pod adresem /ui/v2/login. postgres przechowuje wszystkie dane. Pamięć podręczna Redis oraz kolektor OpenTelemetry znajdują się w tym samym pliku, ale są ukryte za profilami Compose i pozostają wyłączone, dopóki nie zostaną jawnie wywołane.

Nie uruchamiaj jeszcze docker compose up. Pierwszy start tworzy instancję, a szeregu poniższych ustawień nie można później zmienić bez dodatkowej pracy.

Plik .env, który został skopiowany, posiada przypięte własne tagi obrazów:

ZITADEL_VERSION=v4.16.0
TRAEFIK_IMAGE=traefik:v3.7.7
POSTGRES_IMAGE=postgres:17.10-alpine

Obecne wydanie v4 to v4.17.1, opublikowane 14 sierpnia 2026. Ustaw ZITADEL_VERSION na wersję, którą zamierzasz uruchomić, i pozostań w linii v4 zamiast śledzić najnowsze zmiany. Powyższy curl pobiera docker-compose.yml z gałęzi main, która nie jest do niczego przypięta, dlatego zapisz swoją kopię obu plików w repozytorium git. W przeciwnym razie to samo polecenie wykonane na nowej maszynie w przyszłym miesiącu pobierze inny plik, a zmiany pozostaną nieznane.

Nadanie Postgres własnego użytkownika i silnego hasła

Dostarczony .env łączy Zitadel z PostgreSQL jako superużytkownik, używając hasła postgres:

POSTGRES_ADMIN_USER=postgres
POSTGRES_ADMIN_PASSWORD=postgres
ZITADEL_DATABASE_POSTGRES_DSN=postgresql://postgres:postgres@postgres:5432/zitadel?sslmode=disable

Na etapie utwardzania zabezpieczeń występuje pułapka. Dokumentacja Zitadel zaleca dopisanie POSTGRES_ZITADEL_PASSWORD do .env, jednak bazowy docker-compose.yml nigdy nie odczytuje tej zmiennej, więc jej ustawienie nie przynosi efektu. Samodzielna zmiana POSTGRES_ADMIN_PASSWORD powoduje zerwanie połączenia, ponieważ hasło jest zapisane bezpośrednio w ciągu DSN (data source name). DSN to parametr określający sposób łączenia się Zitadel z bazą.

Komentarze w .env.example wyjaśniają to wprost: gdy DSN jest skonfigurowany, Zitadel używa wskazanego użytkownika bezpośrednio i nie tworzy automatycznie konta z ograniczonymi uprawnieniami, dlatego rola musi istnieć przed pierwszym uruchomieniem. Należy wygenerować hasło, uruchomić Postgres i utworzyć rolę.

tr -dc A-Za-z0-9 </dev/urandom | head -c 32; echo

docker compose --env-file .env -f docker-compose.yml up -d postgres

docker compose --env-file .env -f docker-compose.yml exec -T postgres \
  psql -U postgres -d postgres <<'SQL'
CREATE ROLE zitadel LOGIN PASSWORD 'the-password-you-generated';
ALTER DATABASE zitadel OWNER TO zitadel;
SQL

docker compose --env-file .env -f docker-compose.yml exec -T postgres \
  psql -U postgres -d zitadel -c 'ALTER SCHEMA public OWNER TO zitadel;'

Wywołania psql są wykonywane wewnątrz kontenera przez lokalny socket, któremu oficjalny obraz Postgres ufa, więc nie wymagają podania hasła. Kluczowe znaczenie ma własność bazy. W PostgreSQL 15 i nowszych, zwykłe GRANT ALL PRIVILEGES ON DATABASE nie pozwala już roli na tworzenie tabel w schemacie public, przez co faza konfiguracji Zitadel kończy się błędem uprawnień podczas budowania schematów. Uczynienie roli właścicielem bazy danych i schematu rozwiązuje ten problem.

Teraz należy wskazać DSN nową rolę i ustawić silne hasło administratora w pliku:

POSTGRES_ADMIN_PASSWORD=a-32-character-random-string
ZITADEL_DATABASE_POSTGRES_DSN=postgresql://zitadel:the-password-you-generated@postgres:5432/zitadel?sslmode=disable

sslmode=disable jest tutaj dopuszczalne, ponieważ Postgres jest dostępny tylko w prywatnej sieci Compose, a jego port nie jest wystawiony na hosta. Po pierwszym pełnym uruchomieniu należy sprawdzić, czy rola faktycznie jest właścicielem danych:

docker compose exec -T postgres psql -U zitadel -d zitadel -c '\dn'

Wynik powinien zawierać schemat eventstore oraz schemat projections. Pusta lista oznacza, że faza konfiguracji nie została ukończona, a przyczyna znajduje się w logach kontenera API.

Masterkey i konsekwencje jego utraty

Zitadel szyfruje sekrety przed ich zapisaniem: sekrety klientów, dane uwierzytelniające dostawców tożsamości, hasło SMTP, ziarna haseł jednorazowych (OTP) oraz klucze maszynowe. Masterkey odblokowuje wszystkie te dane. Klucz ten ma dokładnie 32 znaki, a dokumentacja jasno określa konsekwencje: nie można go zmienić bez utraty dostępu do zaszyfrowanych danych.

Wygeneruj klucz i zastąp nim linię z symbolem zastępczym w .env:

tr -dc A-Za-z0-9 </dev/urandom | head -c 32; echo

Edytuj linię ZITADEL_MASTERKEY=MasterkeyNeedsToHave32Characters zamiast dopisywać drugą. Compose przyjmuje ostatnią definicję powtórzonego klucza, więc dopisanie zadziała, ale plik zawierający dwie linie masterkey stanowi pułapkę dla kolejnego administratora.

Zastanów się teraz, gdzie przechowywany jest ten klucz. Plik compose uruchamia kontener API w następujący sposób:

command: start-from-init --masterkey "${ZITADEL_MASTERKEY}"

Masterkey znajduje się zatem w wierszu poleceń kontenera, gdzie docker inspect ujawnia go każdemu, kto ma dostęp do gniazda Docker. Na serwerze VPS z jednym administratorem jest to akceptowalny kompromis, a uprawnienia pliku .env chronią go na dysku. Jeśli takie rozwiązanie jest nieakceptowalne, zamontuj klucz jako plik i użyj --masterkeyFile /run/secrets/zitadel-masterkey, co pozwoli uniknąć przekazywania wartości w argumentach procesu.

Przed pierwszym uruchomieniem skopiuj masterkey do menedżera haseł. Nie pojawia się on w zrzucie bazy danych, więc zrzut przywrócony z innym masterkey spowoduje powstanie instancji, która nie będzie w stanie odczytać własnych sekretów. Przechowuj klucz w innym miejscu niż archiwum zawierające zrzut bazy, aby kradzież kopii zapasowej nie oznaczała utraty zarówno zaszyfrowanych danych, jak i klucza do nich.

Ustawienie domeny zewnętrznej przed pierwszym uruchomieniem

ZITADEL_DOMAIN w .env zasila ZITADEL_EXTERNALDOMAIN w kontenerze i jest to nazwa, którą wpisują użytkownicy. Zitadel wywodzi z niej wystawcę OIDC, bazowy URI interfejsu logowania, punkty końcowe SAML oraz nazwę logowania pierwszego administratora, więc nie jest to ustawienie kosmetyczne.

ZITADEL_DOMAIN=auth.example.com
ZITADEL_EXTERNALPORT=443
ZITADEL_EXTERNALSECURE=true

Zitadel rozpoznaje, z którą instancją nawiązywane jest połączenie, na podstawie nagłówka Host. Jeśli nagłówek ten nie pasuje do znanej domeny, każde żądanie otrzymuje tę samą odpowiedź:

ID=QUERY-1kIjX Message=Instance not found

Jest to najczęstszy błąd w samodzielnie hostowanym Zitadel i prawie zawsze oznacza jedną z dwóch rzeczy. Albo ZITADEL_DOMAIN nie jest nazwą, pod którą przeglądasz serwis, albo proxy znajdujące się przed nim nadpisuje Host adresem upstream. Przeglądanie serwera przez adres IP zamiast przez nazwę również wywołuje ten błąd.

Wartości te można zmienić później. Zitadel musi jednak ponownie uruchomić fazę konfiguracji, aby uwzględnić zmianę, a każda zarejestrowana już aplikacja zachowa swoje stare URI przekierowania. Wybór docelowej nazwy już teraz jest znacznie mniej kosztowny niż późniejsza migracja.

Terminacja TLS za pomocą nakładki Let's Encrypt

Dla domeny publicznej należy dodać nakładkę Let's Encrypt dla Zitadel. Przełącza ona Traefik na wyzwanie HTTP ACME (automatic certificate management environment) i zastępuje opublikowane porty portami 80 oraz 443, co oznacza, że żadna inna usługa na serwerze nie może z nich korzystać.

curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.mode-letsencrypt.yml
echo 'LETSENCRYPT_EMAIL=ops@example.com' >> .env

Nakładka ustawia również ZITADEL_EXTERNALPORT: 443 oraz ZITADEL_EXTERNALSECURE: true w kontenerze API, dzięki czemu publiczny adres URL oraz adresy URL generowane przez Zitadel są zgodne. Rekord A musi zostać poprawnie rozwiązany przed uruchomieniem, ponieważ w przeciwnym razie wyzwanie HTTP zakończy się niepowodzeniem.

Jeśli terminacja TLS odbywa się już na Nginx lub na load balancerze, należy użyć docker-compose.mode-external-tls.yml i ustawić TRAEFIK_TRUSTED_IPS na zakresy adresów, z których wysyła ruch proxy. Traefik honoruje nagłówki X-Forwarded-* tylko z adresów znajdujących się na tej liście; błędna wartość spowoduje odrzucenie przekazanego protokołu, a Zitadel zacznie generować adresy URL http:// dla witryny HTTPS.

Proxy nadrzędne (upstream) ma dwa zadania, co do których Zitadel jest rygorystyczny. Musi obsługiwać HTTP/2 w komunikacji z backendem, ponieważ API wykorzystuje gRPC. Musi również przekazywać nagłówek Host w niezmienionej formie wraz z X-Forwarded-Proto: https. Przykład konfiguracji Nginx dla Zitadel przedstawia wymagany format:

server {
    listen 443 ssl;
    http2 on;
    ssl_certificate     /etc/certs/selfsigned.crt;
    ssl_certificate_key /etc/certs/selfsigned.key;
    location /ui/v2/login {
        proxy_pass http://login-external-tls:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
    }
    location / {
        grpc_pass grpc://zitadel-external-tls:8080;
        grpc_set_header Host $host;
        grpc_set_header X-Forwarded-Proto https;
    }
}

Nazwy upstream w powyższym przykładzie odnoszą się do kontenerów w środowisku testowym Zitadel, dlatego należy zastąpić je własnymi. Jeśli Zitadel jest udostępniany na porcie innym niż 443, należy użyć grpc_set_header Host $host:$server_port;, aby numer portu był przekazywany wraz z nagłówkiem. Reszta konfiguracji to standardowy wirtualny host, a analiza konfiguracji reverse proxy Nginx wiersz po wierszu omawia elementy, które nie są specyficzne dla Zitadel.

Pierwszy administrator i wymuszenie zmiany hasła

Pierwsze uruchomienie tworzy jedną instancję, jedną organizację oraz jednego administratora. Nazwa użytkownika to zitadel-admin@ plus zitadel. plus Twoja domena zewnętrzna, więc przy ZITADEL_DOMAIN=auth.example.com wygląda to następująco:

zitadel-admin@zitadel.auth.example.com

Hasło to Password1!, chyba że ustawiono własne. Domyślne ustawienie Zitadel wymusza zmianę hasła przy pierwszym logowaniu, a dostarczony plik compose nadpisuje tę wartość:

ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: false

Ta linia jest wpisana na sztywno w docker-compose.yml zamiast być odczytywaną z .env, dlatego należy umieścić własne wartości w małym pliku nakładki. Nazwij go docker-compose.local.yml:

services:
  zitadel-api:
    environment:
      ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS: you@example.com
      ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: "a-long-temporary-password"
      ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: "true"

Compose ładuje docker-compose.override.yml automatycznie tylko wtedy, gdy jest uruchamiany bez flagi -f, a każde polecenie w przewodniku Zitadel przekazuje -f, co wyłącza to zachowanie. Zamiast powtarzać rosnącą listę flag, przypnij listę plików w .env:

COMPOSE_FILE=docker-compose.yml:docker-compose.mode-letsencrypt.yml:docker-compose.local.yml

Teraz uruchom usługę:

docker compose pull
docker compose up -d --wait

--wait wstrzymuje polecenie do czasu pomyślnego przejścia testów stanu (healthchecks). Jeśli kontener API ich nie przejdzie, Compose zatrzyma się z błędem dependency failed to start: container zitadel-compose-zitadel-api-1 is unhealthy, a docker compose logs zitadel-api zawiera przyczynę. Przy pierwszym uruchomieniu przyczyną jest zazwyczaj długość klucza głównego (masterkey) lub DSN bazy danych.

Zaloguj się pod adresem https://auth.example.com/ui/console, zmień hasło, a następnie włącz drugi składnik uwierzytelniania (2FA) dla tego konta, zanim utworzysz cokolwiek innego. Każda wartość ZITADEL_FIRSTINSTANCE_* ma zastosowanie tylko podczas tworzenia pierwszej instancji. Gdy instancja już istnieje, ich edycja nie przynosi żadnego efektu.

Dlaczego resetowanie hasła nie działa, dopóki nie skonfigurujesz SMTP

Dostawca tożsamości, który nie może wysyłać wiadomości e-mail, jest uszkodzony w sposób, który może pozostać niezauważony przez wiele tygodni. Zitadel wysyła wiadomości e-mail w celu zapraszania użytkowników, weryfikacji adresów, przesyłania linków do resetowania hasła, kodów jednorazowych oraz powiadomień o przejęciu domeny. Bez skonfigurowanego dostawcy SMTP konsola nadal zgłasza wykonanie akcji, a wiadomość trafia do procesu obsługi powiadomień, który nie ma dokąd jej wysłać. Domyślne ustawienia nadają temu procesowi MaxAttempts: 3 oraz MaxTtl: 5m, więc podejmuje on kilka prób w ciągu kilku minut, a następnie przerywa działanie. Osoba oczekująca na link nie otrzymuje żadnej informacji.

Konfigurację przeprowadza się w konsoli, w ustawieniach instancji pod adresem https://auth.example.com/ui/console/settings. Formularz dostawcy SMTP wymaga podania adresu e-mail nadawcy, nazwy nadawcy, hosta i portu, użytkownika, hasła SMTP oraz przełącznika TLS. Przed zapisaniem zmian użyj przycisku testowego w formularzu, ponieważ wysyła on rzeczywistą wiadomość: albo dotrze ona do odbiorcy, albo nie.

Istnieje odpowiedni zestaw zmiennych środowiskowych, ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_HOST i powiązane z nimi zmienne. Mają one zastosowanie w momencie tworzenia instancji. W przypadku działającego stosu nie wywierają one żadnego wpływu, dlatego dla istniejącej instancji właściwym miejscem konfiguracji jest konsola.

Należy zwrócić uwagę na dwie kwestie dotyczące dostarczania wiadomości z serwera VPS, ponieważ to właśnie tam najczęściej występują problemy. Większość dostawców blokuje wychodzący port 25 na nowych kontach, więc bezpośrednia wysyłka do serwera pocztowego odbiorcy kończy się przekroczeniem czasu oczekiwania bez użytecznego komunikatu o błędzie. Zamiast tego należy użyć uwierzytelnionego przekaźnika (relay) na porcie 587. Ponadto należy opublikować rekordy SPF (Sender Policy Framework) oraz DKIM (DomainKeys Identified Mail) dla domeny nadawcy, w przeciwnym razie link do resetowania hasła trafi do spamu, co z perspektywy użytkownika wygląda dokładnie tak, jakby wiadomość nigdy nie została wysłana.

Zweryfikuj działanie przed zaproszeniem kogokolwiek. Utwórz tymczasowego użytkownika, zleć reset hasła i sprawdź, czy wiadomość dotarła. Jeśli nie, docker compose logs -f zitadel-api wskaże przyczynę błędu SMTP. Hasło SMTP jest przechowywane w bazie danych w postaci zaszyfrowanej, co stanowi kolejny element chroniony przez masterkey.

Oddzielne tworzenie kopii zapasowych Postgres i masterkey

Wszystkie dane Zitadel znajdują się w PostgreSQL. Kluczem do ich odszyfrowania jest masterkey. Należy tworzyć kopie zapasowe tych elementów w dwóch różnych lokalizacjach.

Najpierw zrzut bazy danych:

sudo install -d -m 700 /srv/zitadel-backups
docker compose exec -T postgres \
  pg_dump -U postgres -Fc zitadel > "/srv/zitadel-backups/zitadel-$(date +%F).dump"

-Fc to format niestandardowy, który kompresuje dane podczas zapisu i który pg_restore potrafi odczytywać selektywnie. exec -T pomija terminal, co jest istotne, ponieważ proces ten jest uruchamiany przez cron bez przypisanego terminala.

Następnie należy przesłać ten katalog poza serwer za pomocą restic, który szyfruje i deduplikuje dane:

export RESTIC_REPOSITORY="sftp:backup@backup.example.com:/srv/restic/zitadel"
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init
restic backup /srv/zitadel-backups
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

restic init uruchamia się tylko raz, pierwszego dnia. Umieść zrzut oraz dwa ostatnie polecenia w /usr/local/bin/zitadel-backup.sh i uruchamiaj je co noc:

0 3 * * * /usr/local/bin/zitadel-backup.sh

Twórz kopie zapasowe .env oraz wszystkich używanych plików compose w git. Wyjątkiem od tej reguły jest masterkey. Powinien on znajdować się w menedżerze haseł oraz w drugiej lokalizacji, która nie jest tym samym repozytorium restic, ponieważ archiwum przechowujące bazę danych wraz z kluczem deszyfrującym przestaje być kopią zapasową zaszyfrowanego systemu.

Kopia zapasowa, której nie przywrócono, jest tylko przypuszczeniem. Przywróć dane do tymczasowej bazy danych na tym samym serwerze i sprawdź je:

docker compose exec -T postgres createdb -U postgres zitadel_restore_test
docker compose exec -T postgres pg_restore -U postgres -d zitadel_restore_test \
  < /srv/zitadel-backups/zitadel-2026-08-21.dump
docker compose exec -T postgres psql -U postgres -d zitadel_restore_test -c '\dt eventstore.*'
docker compose exec -T postgres dropdb -U postgres zitadel_restore_test

Lista tabel w schemacie eventstore oznacza, że zrzut jest poprawny. Błąd informujący o braku schematu oznacza, że kopia jest nieprawidłowa, a fakt ten został wykryty w dniu, w którym nie wiąże się to z żadnymi kosztami. Ogólny schemat tworzenia kopii zapasowych i aktualizacji stosu Compose ma tutaj niemal niezmienione zastosowanie, a przechowywanie masterkey poza tym samym archiwum jest jedynym elementem specyficznym dla Zitadel.

Aktualizacja Zitadel bez utraty instancji

Aktualizacja to zmiana wersji w .env, po której następują dwa polecenia:

docker compose pull
docker compose up -d --wait

Należy zrozumieć działanie drugiego polecenia przed uruchomieniem go w środowisku produkcyjnym, z którego korzystają użytkownicy. Poleceniem kontenera jest start-from-init, które wykonuje fazy inicjalizacji i konfiguracji przed rozpoczęciem obsługi ruchu; faza konfiguracji obejmuje migracje bazy danych. Zmiana wersji powoduje zatem automatyczne uruchomienie migracji schematu na działającej bazie danych w momencie startu kontenera, podczas gdy --wait oczekuje na pomyślny wynik testu stanu (healthcheck). To główny powód, dla którego test przywracania danych opisany powyżej nie jest opcjonalny.

Należy wykonać świeży zrzut bazy danych bezpośrednio przed aktualizacją. Zrzut z poprzedniej nocy jest niewystarczający.

Nie należy przeskakiwać głównych wersji (major version). Przejście z v3 na v4 wymaga wcześniejszego posiadania wersji v3.4.1 lub nowszej, ponieważ w v4 usunięto starsze klucze podpisywania OIDC, co powoduje, że tokeny podpisane starymi kluczami przestają być weryfikowane w momencie aktualizacji. Biuletyn techniczny Zitadel A-10017 opisuje ten problem; rozwiązaniem jest uruchomienie nowszej wersji v3 na czas wystarczający do wygaśnięcia starych tokenów przed wykonaniem aktualizacji.

Należy monitorować fazę konfiguracji za pomocą docker compose logs -f zitadel-api. Migracje w dużym eventstore trwają kilka minut, a Traefik nie przekieruje ruchu do API, dopóki test stanu nie zakończy się powodzeniem, co oznacza, że w tym czasie serwis będzie niedostępny. Należy zaplanować ten proces, zamiast odkrywać go w trakcie awarii.

Wycofanie zmian (rollback) nie polega jedynie na przywróceniu poprzedniego tagu obrazu. Po wykonaniu migracji starszy plik binarny nie rozpoznaje zmodyfikowanego schematu, dlatego wycofanie zmian wymaga przywrócenia zrzutu bazy danych. Gdy instancja obsługuje rzeczywistych użytkowników, należy przejść na docker-compose.prodlike.yml, czyli overlay, który uruchamia inicjalizację i konfigurację jako kroki oddzielone od startu usługi. Dzięki temu migracja jest procesem wyzwalanym i monitorowanym ręcznie, a nie efektem ubocznym restartu kontenera.

Co wskazać w nowym dostawcy tożsamości

W konsoli należy utworzyć projekt, a następnie aplikację w jego obrębie. Dla nowoczesnych rozwiązań należy wybrać OIDC, a Zitadel wygeneruje identyfikator klienta (client ID), sekret klienta (client secret) oraz dokument discovery pod adresem https://auth.example.com/.well-known/openid-configuration. Większość oprogramowania self-hosted obsługującego logowanie jednokrotne (SSO) wymaga właśnie tych danych.

Wiele aplikacji nie obsługuje tego standardu lub oferuje go tylko w płatnych wersjach. W pierwszym przypadku oauth2-proxy przed aplikacją pozwala zabezpieczyć dowolną usługę HTTP za pomocą Zitadel. W drugim przypadku warto przeczytać podatek od SSO w aplikacjach self-hosted przed zaplanowaniem migracji opartej na funkcji, za którą nie wniesiono opłaty.

FAQ

Ile pamięci RAM i mocy procesora wymaga samodzielnie hostowany Zitadel?

Przewodnik wdrożeniowy Zitadel zaleca około 4 rdzeni procesora oraz 8 GB pamięci RAM dla pojedynczego węzła w ograniczonej konfiguracji, a 16 GB na węzeł przy włączonym logowaniu i metrykach. PostgreSQL wymaga osobnych zasobów, szacunkowo jeden rdzeń na każde 100 żądań na sekundę oraz 4 GB pamięci RAM na rdzeń. Szybki start z użyciem Compose mieści się w 2 GB, co wystarcza do testów, ale jest wartością poniżej zalecanej dla systemów, od których zależą inne usługi.

Co się stanie, jeśli utracę klucz główny (masterkey) Zitadel?

Wszystkie dane zaszyfrowane tym kluczem pozostają zaszyfrowane. Sekrety klientów, poświadczenia dostawców tożsamości, hasło SMTP oraz ziarna haseł jednorazowych (OTP) stają się niemożliwe do odszyfrowania, a klucza nie można zmienić po fakcie. Sam zrzut bazy danych nie pozwala na przywrócenie działającej instancji, ponieważ zawiera on tekst zaszyfrowany bez klucza. Klucz główny należy przechowywać w menedżerze haseł, w miejscu oddzielonym od kopii zapasowej zawierającej zrzut bazy. W przypadku utraty obu elementów jedyną drogą jest ponowna budowa instancji od podstaw.

Dlaczego wiadomości e-mail z resetem hasła Zitadel nie docierają?

Przyczyną jest brak konfiguracji dostawcy SMTP lub niezdolność skonfigurowanego dostawcy do dostarczenia wiadomości. Zitadel kolejkuje każde powiadomienie do procesu roboczego z domyślnie trzema próbami i w obu przypadkach zgłasza sukces w konsoli, dlatego awaria przebiega w sposób niezauważalny. Dostawcę SMTP należy skonfigurować w ustawieniach instancji i użyć przycisku testowego w formularzu, który wysyła rzeczywistą wiadomość. W przypadku VPS należy użyć uwierzytelnionego przekaźnika (relay) na porcie 587, ponieważ większość dostawców blokuje wychodzący port 25. Należy również opublikować rekordy SPF oraz DKIM dla domeny wysyłającej, aby poczta nie była filtrowana jako spam.

Czy po instalacji mogę zmienić zewnętrzną domenę Zitadel?

Tak, ale nie poprzez edycję samego .env. Należy zmienić ZITADEL_EXTERNALDOMAIN, ZITADEL_EXTERNALPORT oraz ZITADEL_EXTERNALSECURE, a następnie pozwolić Zitadel na ponowne uruchomienie fazy konfiguracji, aby zmiany zostały uwzględnione. Aplikacje już zarejestrowane zachowują swoje stare identyfikatory URI przekierowań i muszą zostać zaktualizowane ręcznie. Każde żądanie, którego nagłówek Host nie pasuje do domeny znanej Zitadel, otrzyma odpowiedź Instance not found. Wybór docelowej nazwy przed pierwszym uruchomieniem pozwala uniknąć tych czynności.