SSD Nodes Learn 🎉 VPS $5.50/माह से
गाइड Matt Connorलेखक: Matt Connor

VPS पर Docker के साथ Zitadel कैसे host करें

Zitadel को VPS पर self-host करने के लिए 4 CPU cores और 8GB RAM की आवश्यकता होती है। इस गाइड में PostgreSQL, masterkey, TLS, SMTP सेटअप और डेटाबेस अपग्रेड की पूरी प्रक्रिया जानें।

VPS पर Zitadel को self-host करने के लिए आवश्यक चीजें

VPS पर Zitadel को self-host करने के लिए आपको एक Docker host, उस पर पॉइंट करता हुआ एक public DNS नाम, PostgreSQL, और लगभग 4 CPU cores के साथ 8 GB RAM की आवश्यकता होती है। Zitadel एक identity provider है। यह OIDC (OpenID Connect) और SAML (security assertion markup language) के माध्यम से tokens जारी करता है ताकि आपकी अन्य services को अपनी अलग user list न रखनी पड़े। इंस्टॉलेशन में एक curl और एक docker compose up शामिल है। वे हिस्से जो यह तय करते हैं कि यह सुरक्षित रहेगा या नहीं, वे हैं masterkey, database user, SMTP (simple mail transfer protocol), backup, और पहला upgrade।

नीचे दी गई हर चीज़ Ubuntu 24.04, Docker Engine 24 या उससे नए वर्ज़न (Compose plugin के साथ), और auth.example.com जैसे नाम के पहले से सर्वर पर resolve होने की स्थिति मानकर लिखी गई है।

Zitadel के लिए कितने VPS की आवश्यकता है?

Zitadel के docs में दिया गया Compose quickstart 2 GB RAM की मांग करता है। यह आंकड़ा एक लैपटॉप के लिए है। Zitadel की production guide में अलग आंकड़े दिए गए हैं।

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
  }
]

ये प्रकाशित सिफारिशें हैं, न कि किसी चलते हुए सर्वर से लिए गए माप। इन्हें समस्या के स्वरूप के रूप में देखें। Zitadel process स्वयं छोटी है, जो idle अवस्था में लगभग 0.5 GB RAM लेती है। CPU cores का उपयोग password hashing के लिए होता है, जो जानबूझकर धीमा रखा गया है, इसलिए एक साथ कई logins आने पर CPU usage अचानक बढ़ जाता है। PostgreSQL इस लागत का दूसरा हिस्सा है: वही guide प्रति 100 requests प्रति सेकंड के लिए लगभग एक core और प्रति core 4 GB RAM का बजट बताती है। इन दोनों को मिलाने पर आप 4 cores और 8 GB RAM पर पहुँचते हैं, जो guide में एक single node के लिए बताए गए हैं, या logging और metrics चालू होने पर प्रति node 16 GB RAM की आवश्यकता होती है।

अतः, 2 GB का VPS इस stack को start तो कर देगा, लेकिन यह किसी भी वास्तविक उपयोग के लिए प्रोजेक्ट द्वारा अनुशंसित क्षमता से कम है। Login वह सेवा है जिस पर अन्य सभी सेवाएं निर्भर करती हैं। जब यह down होती है, तो इस पर भरोसा करने वाली कोई भी सेवा किसी को access नहीं देती। यह निर्णय लेना कि 8 GB authentication पर खर्च करने के लिए बहुत अधिक है, एक उचित निर्णय है, और migration के बाद इसे बदलने की तुलना में अभी यह निर्णय लेना काफी सस्ता है। Keycloak, Authentik और Zitadel की तुलना में प्रत्येक के memory और operational कार्य की लागत को कवर किया गया है, और एक self-hosted Authentik server छोटे सर्वर के लिए सामान्य समाधान है।

स्टैक प्राप्त करें और एक वर्ज़न पिन करें

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

वह फ़ाइल उन चार सेवाओं को परिभाषित करती है जिन्हें आप वास्तव में चलाएंगे। Traefik एक reverse proxy है: यह path के आधार पर routing करता है और, नीचे दिए गए overlay के साथ, TLS (transport layer security) को terminate करता है। zitadel-api पोर्ट 8080 पर चलने वाली Go binary है। zitadel-login वह login interface है जो /ui/v2/login पर serve किया जाता है। postgres में सब कुछ समाहित है। एक Redis cache और एक OpenTelemetry collector उसी फ़ाइल में Compose profiles के पीछे स्थित हैं और तब तक बंद रहते हैं जब तक आप उन्हें चालू न करें।

अभी docker compose up को न चलाएं। पहली बार start करने पर instance बनता है, और नीचे दी गई कई सेटिंग्स को बाद में बिना अतिरिक्त मेहनत के बदला नहीं जा सकता।

जो .env आपने कॉपी किया है, वह अपने स्वयं के image tags को पिन करता है:

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

वर्तमान v4 release v4.17.1 है, जिसे 14 August 2026 को प्रकाशित किया गया था। ZITADEL_VERSION को उस वर्ज़न पर सेट करें जिसे आप चलाना चाहते हैं, और जो भी नया वर्ज़न आए उसे ट्रैक करने के बजाय v4 लाइन पर ही बने रहें। ऊपर दिया गया curl, main branch से docker-compose.yml को pull करता है, जो किसी भी वर्ज़न पर पिन नहीं है, इसलिए दोनों फ़ाइलों की अपनी कॉपी को एक git repository में commit करें। अन्यथा, अगले महीने किसी नए बॉक्स पर वही कमांड चलाने पर आपको एक अलग फ़ाइल मिलेगी और आपको पता नहीं चलेगा कि क्या बदलाव हुआ है।

Postgres को अपना अलग user और एक वास्तविक password दें

शipped .env, Zitadel को PostgreSQL से superuser के रूप में जोड़ता है, जिसका password postgres है:

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

यहाँ hardening चरण में एक समस्या है। Zitadel के docs आपको POSTGRES_ZITADEL_PASSWORD को .env में append करने के लिए कहते हैं, लेकिन base docker-compose.yml उस variable को कभी नहीं पढ़ता है, इसलिए उसे set करने से कुछ नहीं बदलता। केवल POSTGRES_ADMIN_PASSWORD को बदलने से connection टूट जाता है, क्योंकि password को DSN (data source name) string के अंदर भी literal रूप में लिखा जाता है। DSN वह line है जो यह तय करती है कि Zitadel कैसे connect होगा।

.env.example में दी गई टिप्पणियाँ बाकी बातें स्पष्ट करती हैं: जब DSN configure किया जाता है, तो Zitadel सीधे उस user का उपयोग करता है और आपके लिए कोई unprivileged user नहीं बनाता है, इसलिए पहली बार start करने से पहले role का अस्तित्व में होना आवश्यक है। एक password generate करें, Postgres को अलग से start करें, और role बनाएँ।

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;'

वे psql calls container के अंदर उसके local socket पर run होते हैं, जिस पर official Postgres image भरोसा करती है, इसलिए वे password नहीं माँगते हैं। Ownership सबसे महत्वपूर्ण हिस्सा है। PostgreSQL 15 और उसके बाद के versions में, एक सामान्य GRANT ALL PRIVILEGES ON DATABASE अब role को public schema में tables बनाने की अनुमति नहीं देता है, इसलिए Zitadel का setup चरण अपने schemas बनाते समय permission error के कारण विफल हो जाता है। Role को database और schema का owner बनाने से यह समस्या हल हो जाती है।

अब DSN को नए role पर point करें, और file में रहते हुए एक वास्तविक admin password set करें:

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 ठीक है क्योंकि Postgres केवल private Compose network पर ही पहुँच योग्य है और इसका port कभी भी host पर publish नहीं किया जाता है। पहली बार पूरी तरह start करने के बाद, जाँचें कि क्या role वास्तव में अपने data का owner है:

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

इससे एक eventstore schema और एक projections schema की सूची आनी चाहिए। यदि सूची खाली है, तो इसका मतलब है कि setup चरण वहाँ तक नहीं पहुँचा, और API container log में इसका कारण मिल जाएगा।

Masterkey और इसे खोने का परिणाम

Zitadel secrets को स्टोर करने से पहले उन्हें encrypt करता है: client secrets, identity provider credentials, SMTP password, one-time-password seeds और machine keys। Masterkey इन सभी को unlock करता है। यह ठीक 32 characters का होता है, और documentation इस परिणाम के बारे में स्पष्ट है: इसे बदले जाने पर encrypted data का access खो जाता है।

एक key generate करें और .env में placeholder line को बदलें:

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

ZITADEL_MASTERKEY=MasterkeyNeedsToHave32Characters line को edit करें, न कि दूसरी line append करें। Compose एक ही key की अंतिम definition को मानता है, इसलिए append करने से काम तो चल जाता है, लेकिन एक ही file में दो masterkey lines रखना भविष्य में पढ़ने वाले के लिए भ्रम पैदा कर सकता है।

अब विचार करें कि यह key कहाँ रहती है। Compose file API container को इस प्रकार start करती है:

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

इसलिए masterkey container command line पर होता है, जहाँ docker inspect इसे Docker socket तक पहुँच रखने वाले किसी भी व्यक्ति को दिखा सकता है। एक single-admin VPS पर यह एक स्वीकार्य समझौता है, और .env का mode इसे disk पर सुरक्षित रखता है। यदि यह स्वीकार्य नहीं है, तो key को file के रूप में mount करें और इसके बजाय --masterkeyFile /run/secrets/zitadel-masterkey का उपयोग करें, जो value को process arguments से बाहर रखता है।

पहली बार start करने से पहले masterkey को अपने password manager में copy कर लें। यह database dump में दिखाई नहीं देता है, इसलिए यदि किसी dump को अलग masterkey के तहत restore किया जाता है, तो वह instance अपने secrets को read नहीं कर पाएगा। इसे उस archive से अलग कहीं रखें जिसमें dump मौजूद है, ताकि एक चोरी हुए backup में encrypted data और उसे खोलने वाली key दोनों एक साथ न हों।

पहली बार start करने से पहले external domain सेट करें

ZITADEL_DOMAIN, .env में ZITADEL_EXTERNALDOMAIN को फीड करता है, और यही वह नाम है जिसे आपके users टाइप करते हैं। Zitadel इसी से OIDC issuer, login interface base URI, SAML endpoints और पहले admin का login नाम निर्धारित करता है, इसलिए यह केवल दिखावटी नहीं है।

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

Zitadel Host header से यह पता लगाता है कि आप किस instance से बात कर रहे हैं। यदि वह header किसी ज्ञात domain से मेल नहीं खाता है, तो हर request का एक ही उत्तर मिलता है:

ID=QUERY-1kIjX Message=Instance not found

यह self-hosted Zitadel की सबसे आम error है, और इसका मतलब लगभग हमेशा दो चीजों में से एक होता है। या तो ZITADEL_DOMAIN वह नाम नहीं है जिस पर आप browse कर रहे हैं, या सामने मौजूद कोई proxy Host को upstream address पर rewrite कर रहा है। नाम के बजाय server के IP address पर browse करने से भी यही error आती है।

आप इन values को बाद में बदल सकते हैं। बदलाव को लागू करने के लिए Zitadel को अपना setup phase फिर से चलाना पड़ता है, और आपके द्वारा पहले से registered हर application अपने पुराने redirect URIs को ही बनाए रखती है। अभी अंतिम नाम चुनना, उसे बाद में बदलने की तुलना में बहुत आसान है।

Let's Encrypt overlay के साथ TLS terminate करें

Public domain के लिए, Zitadel का Let's Encrypt overlay जोड़ें। यह Traefik को ACME (automatic certificate management environment) HTTP challenge पर स्विच कर देता है और published ports को 80 और 443 से बदल देता है, इसलिए सर्वर पर कोई अन्य सर्विस इन ports का उपयोग नहीं कर सकती।

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

यह overlay API container पर ZITADEL_EXTERNALPORT: 443 और ZITADEL_EXTERNALSECURE: true भी सेट करता है, यही कारण है कि public URL और Zitadel द्वारा स्वयं बनाए गए URLs आपस में मेल खाते हैं। शुरू करने से पहले A record का resolve होना आवश्यक है, क्योंकि इसके बिना HTTP challenge विफल हो जाता है।

यदि आप पहले से ही nginx या load balancer पर TLS terminate कर रहे हैं, तो इसके बजाय docker-compose.mode-external-tls.yml का उपयोग करें और TRAEFIK_TRUSTED_IPS को उन ranges पर सेट करें जहाँ से आपका proxy traffic भेजता है। Traefik केवल उस सूची में शामिल addresses से प्राप्त X-Forwarded-* headers का ही सम्मान करता है, इसलिए गलत मान का अर्थ है कि forwarded protocol को हटा दिया जाएगा और Zitadel एक HTTPS साइट के लिए http:// URLs बनाना शुरू कर देगा।

Upstream proxy के दो कार्य हैं जिन्हें लेकर Zitadel सख्त है। इसे backend से HTTP/2 में बात करनी चाहिए, क्योंकि API gRPC है। और इसे X-Forwarded-Proto: https के साथ-साथ Host को भी बिना किसी बदलाव के आगे बढ़ाना चाहिए। Zitadel का अपना nginx उदाहरण इसका स्वरूप दिखाता है:

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;
    }
}

वहाँ दिए गए upstream names Zitadel के test setup में मौजूद containers हैं, इसलिए उन्हें अपने containers से बदलें। यदि आप Zitadel को 443 के अलावा किसी अन्य port पर serve करते हैं, तो grpc_set_header Host $host:$server_port; का उपयोग करें ताकि port header के साथ आगे बढ़ सके। बाकी एक सामान्य virtual host है, और nginx reverse proxy config का पंक्ति-दर-पंक्ति विवरण उन हिस्सों को कवर करता है जो Zitadel-विशिष्ट नहीं हैं।

पहला एडमिन, और पासवर्ड परिवर्तन को बाध्य करना

पहली बार start करने पर एक instance, एक organisation और एक human admin बनता है। लॉगिन नाम zitadel-admin@ और zitadel. और आपके external domain का संयोजन होता है, इसलिए ZITADEL_DOMAIN=auth.example.com के साथ यह इस प्रकार है:

zitadel-admin@zitadel.auth.example.com

पासवर्ड Password1! होता है, जब तक कि आप अपना स्वयं का पासवर्ड सेट न करें। Zitadel का upstream default पहली लॉगिन पर पासवर्ड बदलने के लिए बाध्य करता है, और प्रदान की गई compose file उस default को override करती है:

ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: false

वह लाइन docker-compose.yml में hardcoded है, न कि .env से पढ़ी जाती है, इसलिए अपने स्वयं के मान एक छोटे overlay में रखें। इसे 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 केवल तब docker-compose.override.yml को अपने आप लोड करता है जब आप इसे बिना किसी -f flag के चलाते हैं, और Zitadel की guide में हर command -f पास करती है, जो इसे बंद कर देता है। flags की बढ़ती हुई सूची को बार-बार दोहराने के बजाय, .env में file list को pin करें:

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

अब इसे start करें:

docker compose pull
docker compose up -d --wait

--wait command को तब तक रोक कर रखता है जब तक healthchecks पास न हो जाएं। जब API container वहां तक नहीं पहुंच पाता, तो Compose dependency failed to start: container zitadel-compose-zitadel-api-1 is unhealthy के साथ रुक जाता है, और docker compose logs zitadel-api में इसका कारण होता है। पहली बार start करने पर इसका कारण आमतौर पर masterkey की लंबाई या database DSN होता है।

https://auth.example.com/ui/console पर लॉगिन करें, पासवर्ड बदलें, और फिर कुछ भी अन्य बनाने से पहले उस account के लिए second factor चालू करें। प्रत्येक ZITADEL_FIRSTINSTANCE_* मान केवल तभी लागू होता है जब पहला instance बनाया जा रहा हो। एक बार instance बन जाने के बाद, उन्हें edit करने का कोई प्रभाव नहीं पड़ता।

SMTP काम न करने तक पासवर्ड रीसेट क्यों काम नहीं करता है

एक identity provider जो मेल नहीं भेज सकता, वह इस तरह से खराब होता है कि यह हफ्तों तक पता नहीं चलता। Zitadel उपयोगकर्ता आमंत्रणों, पता सत्यापन, पासवर्ड रीसेट लिंक, वन-टाइम कोड और डोमेन क्लेम नोटिस के लिए ईमेल भेजता है। यदि कोई SMTP provider कॉन्फ़िगर नहीं है, तो भी Console इस क्रिया को पूर्ण बताता है, और संदेश एक notification worker के पास चला जाता है जिसके पास इसे भेजने का कोई स्थान नहीं होता। डिफ़ॉल्ट सेटिंग्स उस worker को MaxAttempts: 3 और MaxTtl: 5m देती हैं, इसलिए यह कुछ मिनटों तक कई बार पुनः प्रयास करता है और फिर रुक जाता है। लिंक की प्रतीक्षा कर रहे व्यक्ति को कुछ भी पता नहीं चलता।

इसे Console में, instance सेटिंग्स के अंतर्गत https://auth.example.com/ui/console/settings पर कॉन्फ़िगर करें। SMTP provider फॉर्म में भेजने वाले का ईमेल पता, भेजने वाले का नाम, host और port, उपयोगकर्ता, SMTP पासवर्ड और TLS टॉगल की आवश्यकता होती है। सेव करने से पहले उस फॉर्म में टेस्ट बटन का उपयोग करें, क्योंकि यह एक वास्तविक संदेश भेजता है: यह या तो पहुँचता है या नहीं।

पर्यावरण वेरिएबल्स (environment variables) का एक संबंधित सेट भी है, ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_HOST और इसके समकक्ष। ये तब लागू होते हैं जब कोई instance बनाया जाता है। पहले से चल रहे stack पर इनका कोई प्रभाव नहीं पड़ता, इसलिए मौजूदा instance के लिए Console ही सही स्थान है।

VPS से डिलीवरी के बारे में दो बातें, क्योंकि यहीं पर यह आमतौर पर विफल होता है। अधिकांश प्रदाता नए खातों पर आउटबाउंड port 25 को ब्लॉक कर देते हैं, इसलिए प्राप्तकर्ता के मेल सर्वर पर सीधा भेजा गया संदेश बिना किसी उपयोगी त्रुटि के टाइम-आउट हो जाता है। इसके बजाय port 587 पर एक authenticated relay का उपयोग करें। और भेजने वाले डोमेन के लिए SPF (sender policy framework) और DKIM (domainkeys identified mail) रिकॉर्ड प्रकाशित करें, अन्यथा रीसेट लिंक स्पैम में चला जाएगा, जो उपयोगकर्ता को ऐसा ही लगेगा जैसे मेल कभी भेजा ही नहीं गया।

किसी को भी आमंत्रित करने से पहले इसे सिद्ध करें। एक अस्थायी उपयोगकर्ता बनाएँ, पासवर्ड रीसेट के लिए अनुरोध करें, और देखें कि संदेश पहुँचता है या नहीं। यदि ऐसा नहीं होता है, तो docker compose logs -f zitadel-api SMTP विफलता का नाम बताता है। SMTP पासवर्ड डेटाबेस में एन्क्रिप्टेड रूप में संग्रहीत होता है, जो एक और ऐसी चीज़ है जिसे masterkey आपके लिए सुरक्षित रखता है।

Postgres और masterkey का अलग-अलग बैकअप लें

Zitadel की सारी जानकारी PostgreSQL में होती है। इसे डिक्रिप्ट करने वाली चीज़ masterkey है। इनका बैकअप दो अलग-अलग जगहों पर रखें।

सबसे पहले डंप लें:

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 कस्टम फॉर्मेट है, जो डेटा बाहर निकालते समय उसे कंप्रेस करता है और जिसे pg_restore चुनिंदा रूप से पढ़ सकता है। exec -T टर्मिनल को हटा देता है, जो महत्वपूर्ण है क्योंकि यह बिना टर्मिनल वाले cron से चलता है।

इसके बाद उस डायरेक्टरी को restic के साथ ऑफसाइट भेजें, जो इसे एन्क्रिप्ट और डुप्लीकेट हटाकर स्टोर करता है:

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 केवल पहले दिन, एक बार चलता है। डंप और अंतिम दो कमांड्स को /usr/local/bin/zitadel-backup.sh में डालें और इसे हर रात चलाएं:

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

.env और आपके द्वारा उपयोग की जाने वाली प्रत्येक compose फाइल का बैकअप git में रखें। masterkey इन सबसे अलग है। इसे आपके पासवर्ड मैनेजर और एक ऐसी दूसरी जगह पर होना चाहिए जो यह restic रिपॉजिटरी न हो, क्योंकि डेटाबेस और उसकी डिक्रिप्शन की को एक साथ रखने वाला आर्काइव एन्क्रिप्टेड सिस्टम का बैकअप नहीं रह जाता।

जिस बैकअप को आपने रिस्टोर नहीं किया है, वह केवल एक अनुमान है। उसी सर्वर पर एक स्क्रैच डेटाबेस में रिस्टोर करें और उसे देखें:

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

eventstore स्कीमा में टेबल्स की सूची का मतलब है कि डंप सही है। यदि यह एरर आता है कि स्कीमा मौजूद नहीं है, तो इसका मतलब है कि डंप सही नहीं है, और आपने यह उस दिन पता लगा लिया है जब इसका कोई नुकसान नहीं है। Compose स्टैक के बैकअप और अपग्रेड का सामान्य पैटर्न यहाँ लगभग बिना किसी बदलाव के लागू होता है, और masterkey को उसी आर्काइव से बाहर रखना ही Zitadel के लिए एकमात्र विशेष हिस्सा है।

Zitadel को instance खोए बिना अपग्रेड करना

अपग्रेड का अर्थ है .env में version bump करना, जिसके बाद दो commands चलानी होती हैं:

docker compose pull
docker compose up -d --wait

दूसरी command को किसी ऐसी चीज़ पर चलाने से पहले समझ लें जहाँ लोग login करते हैं। Container की command start-from-init है, जो सेवा शुरू करने से पहले init और setup चरणों को पूरा करती है, और setup चरण का अर्थ है database migrations। इसलिए, version bump container शुरू होते ही आपके live database पर schema migrations चला देता है, और वह भी बिना किसी निगरानी के, जबकि --wait healthcheck के पूरा होने का इंतज़ार करता है। यही कारण है कि ऊपर बताया गया restore test वैकल्पिक नहीं है।

अपग्रेड से ठीक पहले एक नया dump लें। पिछली रात का dump अलग चीज़ है।

Major version को सीधे न बदलें। v3 से v4 पर जाने के लिए पहले v3.4.1 या उसके बाद के version पर होना आवश्यक है, क्योंकि v4 ने legacy OIDC signing keys को हटा दिया है। इसलिए, जैसे ही आप upgrade करेंगे, पुरानी keys से sign किए गए tokens verify होना बंद हो जाएंगे। Zitadel की technical advisory A-10017 में इसका वर्णन है, और इसका समाधान यह है कि आप नए v3 को तब तक चलाएं जब तक कि पुराने tokens expire न हो जाएं, उसके बाद ही अपग्रेड करें।

docker compose logs -f zitadel-api के साथ setup चरण को monitor करें। एक बड़े eventstore पर migrations में कई मिनट लगते हैं, और Traefik तब तक API पर traffic route नहीं करेगा जब तक कि उसका healthcheck पास न हो जाए, इसलिए उस दौरान साइट down रहेगी। इसे अचानक होने देने के बजाय पहले से plan करें।

Rollback का मतलब केवल पुराने tag को वापस लगाना नहीं है। एक बार migrations चल जाने के बाद, पुराना binary उस schema को नहीं समझ पाता जो उसे मिलता है, इसलिए rollback का अर्थ है dump को restore करना। एक बार जब instance पर वास्तविक users आ जाएं, तो docker-compose.prodlike.yml पर आ जाएं। यह एक ऐसा overlay है जो init और setup को start से अलग चरणों के रूप में चलाता है, ताकि migration एक ऐसी प्रक्रिया बन जाए जिसे आप container restart के side effect के बजाय खुद trigger करें और monitor कर सकें।

अपने नए identity provider को कहाँ पॉइंट करें

Console में, एक project बनाएँ और उसके अंदर एक application बनाएँ। किसी भी आधुनिक उपयोग के लिए OIDC चुनें, और Zitadel आपको एक client ID, एक client secret और https://auth.example.com/.well-known/openid-configuration पर एक discovery document देगा। single sign-on का समर्थन करने वाले अधिकांश self-hosted software को ठीक इन्हीं की आवश्यकता होती है।

बहुत से software इसका समर्थन नहीं करते हैं, या केवल paid tier में ही इसका समर्थन करते हैं। पहले मामले के लिए, app के सामने oauth2-proxy किसी भी HTTP service को ऐसी चीज़ में बदल देता है जिसे Zitadel सुरक्षित रख सके। दूसरे मामले के लिए, self-hosted apps में SSO tax को पढ़ना उपयोगी है, इससे पहले कि आप किसी ऐसे feature के आधार पर migration की योजना बनाएँ जिसके लिए आपने भुगतान नहीं किया है।

FAQ

Zitadel को self-host करने के लिए कितनी RAM और CPU की आवश्यकता होती है?

Zitadel की production guide एक reduced setup चलाने वाले single node के लिए लगभग 4 CPU cores और 8 GB RAM की सिफारिश करती है। यदि logging और metrics चालू हैं, तो प्रति node 16 GB RAM की आवश्यकता होती है। PostgreSQL के लिए अलग से बजट रखना चाहिए, जो प्रति 100 requests प्रति सेकंड लगभग एक core और प्रति core 4 GB RAM लेता है। Compose quickstart 2 GB के भीतर शुरू हो जाता है, जो इसे आज़माने के लिए पर्याप्त है, लेकिन यह उस क्षमता से कम है जिसकी सिफारिश उन systems के लिए की जाती है जिन पर अन्य services निर्भर करती हैं।

यदि मैं Zitadel masterkey खो दूँ तो क्या होगा?

इसके साथ encrypt किया गया सब कुछ encrypted ही रहेगा। Client secrets, identity provider credentials, SMTP password और one-time-password seeds को decrypt नहीं किया जा सकता है, और key को बाद में बदला नहीं जा सकता है। केवल database dump से एक working instance को restore नहीं किया जा सकता, क्योंकि dump में ciphertext होता है और कोई key नहीं होती। Masterkey को एक password manager में रखें, और उसे उस backup से अलग स्थान पर रखें जिसमें dump मौजूद है। यदि दोनों खो जाते हैं, तो instance को शुरू से फिर से बनाना ही एकमात्र विकल्प बचता है।

Zitadel password reset emails क्यों नहीं पहुँचते हैं?

इसका कारण यह है कि या तो कोई SMTP provider configure नहीं किया गया है, या जो configure किया गया है वह deliver नहीं कर पा रहा है। Zitadel डिफ़ॉल्ट रूप से प्रत्येक notification को तीन प्रयासों के साथ एक worker को queue करता है और Console में सफलता की सूचना देता है, इसलिए विफलता का पता नहीं चलता है। Instance settings के अंतर्गत SMTP provider को configure करें और उस form में दिए गए test button का उपयोग करें, जो एक वास्तविक message भेजता है। VPS से, port 587 पर एक authenticated relay का उपयोग करें, क्योंकि अधिकांश providers outbound port 25 को block करते हैं। साथ ही, भेजने वाले domain के लिए SPF और DKIM records प्रकाशित करें ताकि mail को spam के रूप में filter न किया जाए।

क्या मैं install करने के बाद Zitadel external domain बदल सकता हूँ?

हाँ, लेकिन केवल .env को edit करके नहीं। ZITADEL_EXTERNALDOMAIN, ZITADEL_EXTERNALPORT और ZITADEL_EXTERNALSECURE को बदलें, और फिर Zitadel को अपना setup phase फिर से चलाने दें ताकि वह बदलाव को लागू कर सके। आपके द्वारा पहले से registered applications अपने पुराने redirect URIs को बनाए रखती हैं और उन्हें हाथ से update करना होगा। इसके अलावा, कोई भी request जिसका Host header Zitadel द्वारा ज्ञात domain से मेल नहीं खाता है, उसे Instance not found error प्राप्त होगा। पहली बार start करने से पहले ही अंतिम नाम चुन लेने से इन सभी समस्याओं से बचा जा सकता है।