Come installare Zitadel su un VPS con Docker
Zitadel richiede 4 core e 8 GB di RAM: configura PostgreSQL, masterkey, TLS, SMTP e backup su un VPS, verificando cosa cambia nel database dopo un upgrade.
Cosa serve per eseguire Zitadel in self-hosting su un VPS
Per eseguire Zitadel in self-hosting su un VPS servono un host Docker, un nome DNS pubblico che punti al server, PostgreSQL e circa 4 core CPU con 8 GB di RAM. Zitadel è un identity provider. Emette token tramite OIDC (OpenID Connect) e SAML (security assertion markup language), così gli altri servizi non devono più gestire elenchi utenti separati. L'installazione consiste in un curl e un docker compose up. Gli elementi che determinano la continuità operativa sono la masterkey, l'utente del database, SMTP (simple mail transfer protocol), il backup e il primo aggiornamento.
Le istruzioni seguenti presuppongono Ubuntu 24.04, Docker Engine 24 o una versione successiva con il plugin Compose e un nome come auth.example.com già risolto verso il server.
Quante risorse VPS richiede Zitadel?
La guida introduttiva con Compose nella documentazione di Zitadel richiede 2 GB di RAM. Questo valore è pensato per un laptop. La guida alla produzione di Zitadel indica valori diversi.
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
}
]Si tratta di raccomandazioni pubblicate, non di misurazioni eseguite su un server in funzione. Usatele per capire la struttura del problema. Il processo di Zitadel utilizza da solo circa 0.5 GB di RAM a riposo. I core servono per l'hashing delle password, che è intenzionalmente lento; per questo un picco di accessi genera un picco di utilizzo della CPU. PostgreSQL rappresenta l'altra parte del fabbisogno: la stessa guida prevede circa un core ogni 100 richieste al secondo e 4 GB di RAM per core. Considerando insieme questi valori, si arriva ai 4 core e ai 8 GB indicati dalla guida per un singolo nodo, oppure a 16 GB per nodo quando logging e metriche sono attivi.
Un VPS da 2 GB avvierà quindi questo stack, ma resta al di sotto dei requisiti raccomandati dal progetto per qualsiasi utilizzo reale. Il servizio di accesso è la dipendenza di tutti gli altri servizi. Quando è inattivo, nessun servizio che si affida a esso consente l'accesso. Decidere che 8 GB sono più di quanto si desidera spendere per l'autenticazione è una scelta ragionevole ed è molto meno costoso farla ora che dopo una migrazione. Il confronto tra Keycloak, Authentik e Zitadel descrive il costo di ciascuna soluzione in termini di memoria e di attività operative, mentre un server Authentik self-hosted è generalmente la risposta più adatta per un server di dimensioni ridotte.
Recuperare lo stack e bloccare una versione
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 .envQuel file definisce quattro servizi che verranno effettivamente eseguiti. Traefik è il reverse proxy: instrada le richieste in base al percorso e, con l’overlay più avanti, gestisce la terminazione TLS (transport layer security). zitadel-api è il binario Go in ascolto sulla porta 8080. zitadel-login è l’interfaccia di accesso disponibile all’indirizzo /ui/v2/login. postgres contiene tutti i dati. Nello stesso file sono presenti anche una cache Redis e un collector OpenTelemetry, gestiti tramite i profili Compose e disattivati finché non li abiliti.
Non eseguire ancora docker compose up. Il primo avvio crea l’istanza e diverse impostazioni riportate di seguito non possono essere modificate in seguito senza interventi aggiuntivi.
Il .env copiato blocca i tag delle proprie immagini:
ZITADEL_VERSION=v4.16.0
TRAEFIK_IMAGE=traefik:v3.7.7
POSTGRES_IMAGE=postgres:17.10-alpineLa release v4 attuale è v4.17.1, pubblicata il 14 agosto 2026. Imposta ZITADEL_VERSION sulla versione che intendi eseguire e resta sulla linea v4 invece di seguire automaticamente la versione più recente. Il curl riportato sopra recupera docker-compose.yml dal ramo main, che non è vincolato a una versione; esegui quindi il commit di entrambi i file in un repository git. In caso contrario, lo stesso comando eseguito su un nuovo server il mese prossimo produrrà un file diverso e non saprai che cosa è cambiato.
Assegna a Postgres un utente dedicato e una password reale
Il file .env fornito configura la connessione di Zitadel a PostgreSQL usando il superuser, con la password postgres:
POSTGRES_ADMIN_USER=postgres
POSTGRES_ADMIN_PASSWORD=postgres
ZITADEL_DATABASE_POSTGRES_DSN=postgresql://postgres:postgres@postgres:5432/zitadel?sslmode=disableIn questa fase di hardening c'è un problema. La documentazione di Zitadel indica di aggiungere POSTGRES_ZITADEL_PASSWORD a .env, ma il docker-compose.yml di base non legge mai quella variabile. Impostarla, quindi, non cambia nulla. Modificare soltanto POSTGRES_ADMIN_PASSWORD interrompe invece la connessione, perché la password è scritta anche direttamente nella stringa DSN (data source name). La DSN determina come Zitadel si connette.
I commenti in .env.example spiegano chiaramente il comportamento restante: quando è configurata una DSN, Zitadel usa direttamente quell'utente e non ne crea automaticamente uno senza privilegi. Il ruolo deve quindi esistere prima del primo avvio. Genera una password, avvia Postgres da solo e crea il ruolo.
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;'Le chiamate psql vengono eseguite all'interno del container tramite il socket locale. L'immagine ufficiale di Postgres considera attendibili queste connessioni, quindi non richiede una password. L'aspetto importante è la proprietà. In PostgreSQL 15 e versioni successive, un semplice GRANT ALL PRIVILEGES ON DATABASE non consente più a un ruolo di creare tabelle nello schema public. La fase di setup di Zitadel fallisce quindi con un errore di autorizzazione durante la creazione degli schemi. Assegnando al ruolo la proprietà del database e dello schema si evita il problema.
Ora modifica la DSN per usare il nuovo ruolo e imposta una password admin reale nello stesso file:
POSTGRES_ADMIN_PASSWORD=a-32-character-random-string
ZITADEL_DATABASE_POSTGRES_DSN=postgresql://zitadel:the-password-you-generated@postgres:5432/zitadel?sslmode=disablesslmode=disable va bene in questo caso, perché Postgres è raggiungibile soltanto sulla rete privata di Compose e la sua porta non viene pubblicata sull'host. Dopo il primo avvio completo, verifica che il ruolo sia effettivamente proprietario dei dati:
docker compose exec -T postgres psql -U zitadel -d zitadel -c '\dn'Dovrebbero essere elencati uno schema eventstore e uno schema projections. Un elenco vuoto indica che la fase di setup non è arrivata a questo punto. Il log del container API ne indica il motivo.
La masterkey e il costo della sua perdita
Zitadel cifra i secret prima di salvarli: secret dei client, credenziali dei provider di identità, password SMTP, seed per le one-time password e chiavi delle macchine. La masterkey consente di accedere a tutti questi dati. È composta esattamente da 32 caratteri e la documentazione è chiara sulle conseguenze: non può essere modificata senza perdere l'accesso ai dati cifrati.
Generane una e sostituisci la riga segnaposto in .env:
tr -dc A-Za-z0-9 </dev/urandom | head -c 32; echoModifica la riga ZITADEL_MASTERKEY=MasterkeyNeedsToHave32Characters invece di aggiungerne una seconda. Compose usa l'ultima definizione di una chiave ripetuta, quindi l'aggiunta funziona, ma un file con due righe masterkey può creare confusione a chi dovrà leggerlo in seguito.
Considera ora dove risiede questa chiave. Il file Compose avvia il container API in questo modo:
command: start-from-init --masterkey "${ZITADEL_MASTERKEY}"La masterkey si trova quindi nella riga di comando del container, dove docker inspect la mostra a chiunque possa raggiungere il socket Docker. Su un VPS gestito da un solo amministratore, questo compromesso è accettabile; la modalità impostata in .env è ciò che la protegge sul disco. Se non è accettabile, monta la chiave come file e usa invece --masterkeyFile /run/secrets/zitadel-masterkey, mantenendo il valore fuori dagli argomenti del processo.
Copia la masterkey nel password manager prima del primo avvio. Non compare in un dump del database, quindi un dump ripristinato con una masterkey diversa produce un'istanza che non può leggere i propri secret. Conservala in un luogo diverso dall'archivio che contiene il dump, in modo che un solo backup sottratto non contenga sia i dati cifrati sia la chiave necessaria per leggerli.
Imposta il dominio esterno prima del primo avvio
ZITADEL_DOMAIN in .env alimenta ZITADEL_EXTERNALDOMAIN nel container ed è il nome che gli utenti devono digitare. Zitadel ricava da questo valore l'emittente OIDC, l'URI di base dell'interfaccia di accesso, gli endpoint SAML e il nome di accesso del primo amministratore. Non è quindi un valore puramente descrittivo.
ZITADEL_DOMAIN=auth.example.com
ZITADEL_EXTERNALPORT=443
ZITADEL_EXTERNALSECURE=trueZitadel identifica l'istanza a cui ti stai connettendo dall'header Host. Se l'header non corrisponde a un dominio conosciuto, ogni richiesta restituisce la stessa risposta:
ID=QUERY-1kIjX Message=Instance not foundQuesto è l'errore più comune nelle installazioni self-hosted di Zitadel e indica quasi sempre una di due condizioni. Oppure ZITADEL_DOMAIN non corrisponde al nome che stai usando per accedere, oppure un proxy interposto modifica Host sostituendolo con l'indirizzo upstream. L'errore si verifica anche quando accedi usando l'indirizzo IP del server invece del nome.
Puoi modificare questi valori in seguito. Zitadel deve eseguire nuovamente la fase di configurazione per applicare la modifica e ogni applicazione già registrata conserva i propri URI di reindirizzamento precedenti. Scegliere subito il nome definitivo è molto più semplice che cambiarlo in seguito.
Terminare TLS con l'overlay Let's Encrypt
Per un dominio pubblico, aggiungi l'overlay Let's Encrypt di Zitadel. L'overlay configura Traefik per usare la challenge HTTP di ACME (ambiente per la gestione automatica dei certificati) e sostituisce le porte pubblicate con 80 e 443. Nessun altro servizio sul server deve quindi occupare una di queste porte.
curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.mode-letsencrypt.yml
echo 'LETSENCRYPT_EMAIL=ops@example.com' >> .envL'overlay imposta inoltre ZITADEL_EXTERNALPORT: 443 e ZITADEL_EXTERNALSECURE: true sul container API. Per questo l'URL pubblico e gli URL che Zitadel genera per il proprio utilizzo coincidono. Prima di avviare il servizio, il record A deve risolvere correttamente, perché la challenge HTTP non funziona senza questa condizione.
Se termini già TLS su nginx o su un load balancer, usa invece docker-compose.mode-external-tls.yml e imposta TRAEFIK_TRUSTED_IPS sugli intervalli di indirizzi dai quali il proxy inoltra le richieste. Traefik considera gli header X-Forwarded-* soltanto se provengono da indirizzi inclusi in quell'elenco. Un valore errato fa scartare il protocollo inoltrato e Zitadel inizia a generare URL http:// per un sito HTTPS.
Un proxy upstream deve svolgere due funzioni che Zitadel richiede esplicitamente. Deve usare HTTP/2 per comunicare con il backend, perché l'API usa gRPC. Deve inoltre inoltrare Host senza modificarlo, insieme a X-Forwarded-Proto: https. L'esempio nginx fornito da Zitadel ha questa struttura:
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;
}
}I nomi upstream presenti nell'esempio corrispondono ai container della configurazione di test di Zitadel. Sostituiscili quindi con quelli del tuo ambiente. Se pubblichi Zitadel su una porta diversa da 443, usa grpc_set_header Host $host:$server_port; per includere la porta nell'header. Il resto è un virtual host standard; una configurazione di reverse proxy nginx analizzata riga per riga descrive le parti non specifiche di Zitadel.
Primo amministratore e modifica obbligatoria della password
Il primo avvio crea una istanza, una organizzazione e un amministratore umano. Il nome di accesso è zitadel-admin@ più zitadel. più il dominio esterno, quindi con ZITADEL_DOMAIN=auth.example.com è:
zitadel-admin@zitadel.auth.example.comLa password è Password1!, a meno che non ne imposti una personalizzata. Il comportamento predefinito upstream di Zitadel impone di modificarla al primo accesso, ma il file Compose fornito disabilita questo comportamento:
ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: falseQuella riga è codificata direttamente in docker-compose.yml e non viene letta da .env. Inserisci quindi i tuoi valori in un piccolo overlay personale. Chiamalo 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 carica automaticamente docker-compose.override.yml solo quando lo esegui senza il flag -f. Tutti i comandi della guida di Zitadel specificano -f, disabilitando questo comportamento. Invece di ripetere un elenco di flag sempre più lungo, fissa l’elenco dei file in .env:
COMPOSE_FILE=docker-compose.yml:docker-compose.mode-letsencrypt.yml:docker-compose.local.ymlOra avvialo:
docker compose pull
docker compose up -d --wait--wait mantiene il comando in attesa finché i controlli di integrità non hanno esito positivo. Se il container API non raggiunge mai questo stato, Compose si interrompe con dependency failed to start: container zitadel-compose-zitadel-api-1 is unhealthy e docker compose logs zitadel-api contiene la causa. Al primo avvio, la causa è solitamente la lunghezza della masterkey o il DSN del database.
Accedi a https://auth.example.com/ui/console, modifica la password e attiva quindi un secondo fattore per quell’account prima di creare qualsiasi altro elemento. Ogni valore ZITADEL_FIRSTINSTANCE_* viene applicato solo durante la creazione della prima istanza. Dopo la creazione dell’istanza, modificarli non produce alcun effetto.
Perché il ripristino della password non produce alcun risultato finché SMTP non funziona
Un identity provider che non riesce a inviare email presenta un problema che può restare nascosto per settimane. Zitadel invia email per gli inviti agli utenti, la verifica degli indirizzi, i link di ripristino della password, i codici monouso e gli avvisi relativi alla rivendicazione dei domini. Se non è configurato alcun provider SMTP, la Console continua a segnalare l'operazione come completata, mentre il messaggio viene affidato a un worker di notifica che non ha alcuna destinazione a cui inviarlo. Per impostazione predefinita, il worker ha MaxAttempts: 3 e MaxTtl: 5m, quindi riprova alcune volte nell'arco di pochi minuti e poi si arresta. L'utente in attesa del link non riceve alcuna indicazione.
Configurare il servizio nella Console, nelle impostazioni dell'istanza all'indirizzo https://auth.example.com/ui/console/settings. Il modulo del provider SMTP richiede un indirizzo email mittente, un nome mittente, host e porta, un utente, una password SMTP e un'opzione per TLS. Usare il pulsante di test del modulo prima di salvare, perché invia un messaggio reale: il messaggio arriva oppure non arriva.
È disponibile anche un gruppo corrispondente di variabili d'ambiente, ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_HOST e le relative variabili. Vengono applicate quando si crea un'istanza. In uno stack già in esecuzione non hanno effetto, quindi per un'istanza esistente la Console è il punto corretto in cui configurare il servizio.
Due aspetti dell'invio da un VPS sono particolarmente importanti, perché è qui che il servizio fallisce più spesso. La maggior parte dei provider blocca la porta 25 in uscita sui nuovi account, quindi l'invio diretto al server email del destinatario va in timeout senza restituire un errore utile. Usare invece un relay autenticato sulla porta 587. Pubblicare inoltre i record SPF (sender policy framework) e DKIM (domainkeys identified mail) per il dominio mittente; in caso contrario, il link di ripristino finisce nello spam e per l'utente il risultato è identico a un messaggio mai inviato.
Verificare il funzionamento prima di invitare qualcuno. Creare un utente temporaneo, richiedere il ripristino della password e verificare che il messaggio arrivi. Se non arriva, docker compose logs -f zitadel-api identifica l'errore SMTP. La password SMTP viene archiviata in forma cifrata nel database; è un altro elemento che masterkey protegge per conto dell'utente.
Eseguire il backup di PostgreSQL e della masterkey separatamente
Tutto ciò che Zitadel conosce si trova in PostgreSQL. La masterkey consente di decrittografarlo. Eseguire il backup dei due elementi in posizioni diverse.
Per prima cosa creare il dump:
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 è il formato personalizzato. Comprimo i dati durante l'esportazione e pg_restore può leggerli in modo selettivo. exec -T disattiva il terminale, aspetto importante perché il comando viene eseguito da cron senza un terminale associato.
Copiare quindi la directory fuori sede con restic, che esegue crittografia e deduplicazione:
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 --prunerestic init viene eseguito una sola volta, soltanto il primo giorno. Inserire il dump e gli ultimi due comandi in /usr/local/bin/zitadel-backup.sh ed eseguirlo ogni notte:
0 3 * * * /usr/local/bin/zitadel-backup.shEseguire il backup di .env e di ogni file Compose utilizzato, archiviandoli in git. La masterkey è l'unica eccezione. Deve essere conservata nel password manager e in una seconda posizione diversa da questo repository restic, perché un archivio che contiene insieme il database e la relativa chiave di decrittografia non costituisce più il backup di un sistema crittografato.
Un backup che non è mai stato ripristinato è soltanto un'ipotesi. Ripristinarlo in un database temporaneo sullo stesso server e verificarne il contenuto:
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_testL'elenco delle tabelle nello schema eventstore indica che il dump è valido. Un errore che segnala l'assenza dello schema indica invece che il dump non è valido. È possibile scoprirlo in una giornata in cui l'errore non comporta alcun costo. Lo schema generale per eseguire il backup e l'upgrade di uno stack Compose si applica qui quasi senza modifiche. L'unico aspetto specifico di Zitadel è mantenere la masterkey fuori dallo stesso archivio.
Aggiornare Zitadel senza perdere l'istanza
Un aggiornamento consiste nell'incrementare la versione in .env, quindi nell'eseguire due comandi:
docker compose pull
docker compose up -d --waitPrima di eseguire il secondo comando su un'istanza utilizzata dagli utenti, è necessario comprenderne il comportamento. Il comando del container è start-from-init: esegue le fasi di init e setup prima di iniziare a gestire le richieste. La fase di setup esegue le migrazioni del database. Di conseguenza, un incremento di versione esegue automaticamente le migrazioni dello schema sul database in produzione all'avvio del container, mentre --wait resta in attesa di un healthcheck. È proprio per questo che il test di ripristino descritto sopra è obbligatorio.
Eseguire un dump aggiornato subito prima dell'aggiornamento. Il dump della notte precedente è un'altra cosa.
Non saltare una major version. Per passare dalla v3 alla v4, è necessario usare prima la v3.4.1 o una versione successiva, perché la v4 ha rimosso le chiavi di firma OIDC legacy. Di conseguenza, i token firmati con le vecchie chiavi non vengono più verificati non appena si completa il passaggio. L'avviso tecnico A-10017 di Zitadel descrive il problema. La correzione consiste nell'eseguire la nuova v3 abbastanza a lungo da consentire la scadenza dei token precedenti, quindi procedere con l'aggiornamento.
Monitorare la fase di setup con docker compose logs -f zitadel-api. Le migrazioni di un eventstore di grandi dimensioni possono richiedere alcuni minuti. Traefik non inoltrerà le richieste all'API finché l'healthcheck non avrà esito positivo, quindi il sito resterà non disponibile durante questo intervallo. Pianificare l'interruzione evita di scoprirla durante l'aggiornamento.
Il rollback non consiste nel ripristinare il tag precedente. Dopo l'esecuzione delle migrazioni, il binario precedente non è in grado di interpretare lo schema trovato. Per eseguire il rollback è quindi necessario ripristinare il dump. Quando l'istanza contiene utenti reali, passare a docker-compose.prodlike.yml, l'overlay che esegue init e setup come passaggi separati dall'avvio. In questo modo una migrazione viene attivata e monitorata esplicitamente, invece di essere un effetto collaterale del riavvio del container.
Cosa configurare per il nuovo identity provider
Nella Console, crea un progetto e poi un'applicazione al suo interno. Scegli OIDC per qualsiasi applicazione moderna: Zitadel fornisce un client ID, un client secret e un documento di discovery all'indirizzo https://auth.example.com/.well-known/openid-configuration. La maggior parte dei software self-hosted che supportano il single sign-on richiede esattamente questi elementi.
Molti software non supportano questa integrazione oppure la rendono disponibile solo in un piano a pagamento. Nel primo caso, oauth2-proxy davanti all'applicazione trasforma qualsiasi servizio HTTP in un servizio che Zitadel può proteggere. Nel secondo caso, vale la pena leggere il costo del single sign-on nelle applicazioni self-hosted prima di pianificare una migrazione basata su una funzionalità per la quale non hai acquistato una licenza.
FAQ
Quanta RAM e CPU richiede un'istanza Zitadel self-hosted?
La guida per gli ambienti di produzione di Zitadel consiglia circa 4 core CPU e 8 GB di RAM per un singolo nodo con una configurazione ridotta, oppure 16 GB per nodo con logging e metriche abilitati. PostgreSQL viene dimensionato separatamente, con circa un core ogni 100 richieste al secondo e 4 GB di RAM per core. Il quickstart Compose si avvia con meno di 2 GB, una quantità sufficiente per le prove ma inferiore a quella consigliata dal progetto per un sistema da cui dipendono altri servizi.
Cosa succede se perdo la masterkey di Zitadel?
Tutto ciò che è stato cifrato con quella chiave resta cifrato. I secret dei client, le credenziali dei provider di identità, la password SMTP e i seed delle password monouso non possono essere decifrati e la chiave non può essere sostituita in seguito. Un dump del database, da solo, non ripristina un'istanza funzionante, perché contiene il testo cifrato ma non la chiave. Conserva la masterkey in un password manager e in un luogo separato dal backup che contiene il dump. Se entrambi vanno persi, l'unica soluzione è ricostruire l'istanza da zero.
Perché le email di reimpostazione della password di Zitadel non arrivano mai?
Perché non è configurato alcun provider SMTP oppure quello configurato non riesce a consegnare i messaggi. Per impostazione predefinita, Zitadel accoda ogni notifica a un worker con tre tentativi e segnala comunque l'esito positivo nella Console, quindi l'errore non è visibile. Configura il provider SMTP nelle impostazioni dell'istanza e usa il pulsante di test presente nel modulo: verrà inviato un messaggio reale. Da un VPS, usa un relay autenticato sulla porta 587, perché la maggior parte dei provider blocca la porta 25 in uscita. Pubblica inoltre i record SPF e DKIM per il dominio di invio, così il messaggio non viene filtrato come spam.
Posso modificare il dominio esterno di Zitadel dopo l'installazione?
Sì, ma non modificando soltanto .env. Modifica ZITADEL_EXTERNALDOMAIN, ZITADEL_EXTERNALPORT e ZITADEL_EXTERNALSECURE, quindi lascia che Zitadel esegua nuovamente la fase di setup per recepire la modifica. Le applicazioni già registrate mantengono i vecchi URI di redirect e devono essere aggiornate manualmente. Inoltre, ogni richiesta il cui header Host non corrisponde a un dominio noto a Zitadel riceve come risposta Instance not found. Scegliere il nome definitivo prima del primo avvio evita tutti questi problemi.