[{"data":1,"prerenderedAt":235},["ShallowReactive",2],{"seo-verification":3,"blog-self-hosting-zitadel-vps-multi-tenant-sso-en":6},{"google":4,"bing":5},"EycwPY2XMyTkVzas3n1ygeNJFGAH513qrMjfDljzsMQ","",{"key":7,"data":8},"blog-self-hosting-zitadel-vps-multi-tenant-sso-en",{"id":9,"slug":10,"slugs":11,"title":15,"excerpt":16,"readTime":17,"views":18,"isPinned":19,"publishedAt":20,"updatedAt":21,"category":22,"categories":28,"featuredImage":30,"bgImage":31,"posterImage":32,"relatedSolution":33,"intro":36,"sections":37,"ctaTitle":30,"ctaBody":30,"ctaButton":153,"ctaUrl":154,"relatedPosts":155},435,"self-hosting-zitadel-vps-multi-tenant-sso",{"fr":12,"en":10,"ar":13,"es":14},"zitadel-sso-self-hosted-vps","استضافة-zitadel-على-vps-sso-متعدد-المستأجرين","zitadel-vps-sso-multi-tenant-sin-keycloak","Self-Hosting ZITADEL on a VPS: Multi-Tenant SSO Without Keycloak","Deploy ZITADEL on your VPS with Docker Compose: cloud-native IAM in Go, native multi-tenancy, OIDC, OAuth2, SAML and Passkeys — without Keycloak's complexity.",14,0,false,"2026-10-12T00:00:00+00:00","2026-10-13T14:10:28+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},8,"Security & Monitoring","securite-monitoring","bg-rose-500\u002F10 text-rose-400","security",[29],{"id":23,"name":24,"slug":25,"color":26,"icon":27},null,"\u002Fblog\u002Fcovers\u002Fbg.svg","\u002Fblog\u002Fcovers\u002Fzitadel-sso-self-hosted-vps-poster.svg",{"categorySlug":34,"appSlug":35},"cybersecurity-bastion","zitadel","ZITADEL is a cloud-native identity provider written in Go, built from the ground up for B2B teams managing multiple clients or distinct projects. Where Keycloak requires configuring a realm per tenant — with all the configuration drift risk that entails — and where Authentik treats multi-tenancy as an additional feature, ZITADEL places organizations and projects at the core of its data model. Each tenant has its own users, its own authentication rules, and its own applications, without ever interfering with neighbors.\n\nIts memory footprint is that of a Go binary — 150 to 300 MB for the full stack with PostgreSQL, versus 512 MB to 1 GB of JVM for Keycloak at rest. It supports OIDC, OAuth2, SAML 2.0, LDAP and WebAuthn Passkeys since version 2.x, with a gRPC\u002FREST API that allows full automation of tenant management without ever touching a graphical interface.",[38,42,54,57,91,94,97,143,147,150],{"type":39,"title":40,"body":41},"h2","ZITADEL vs Keycloak vs Authentik: which to choose for a B2B SaaS","The question comes up systematically whenever an agency or SaaS developer wants to centralize authentication for multiple clients: stick with Keycloak, switch to Authentik, or look at ZITADEL?\n\nKeycloak is the historical standard, feature-rich and backed by Red Hat. It covers OIDC, SAML, LDAP, Kerberos, WebAuthn and has exhaustive documentation. Its problem for self-hosted VPS: the JVM. At rest, Keycloak consumes between 512 MB and 1 GB of RAM, and each realm is a separate configuration tree to maintain. Week 38 of 2026 illustrated the risk: six CVEs fixed in two consecutive releases, including two unauthenticated DoS attacks on SAML and login endpoints.\n\nAuthentik (2026.8.2) is the natural choice for teams wanting a full IdP with a graphical interface, visual flows and SCIM provisioning. It is written in Python\u002FDjango with a TypeScript frontend, and its full stack requires at least 1 GB of usable RAM. Multi-tenancy exists, but is implemented via separate tenants or groups — not via a dedicated data model.\n\nZITADEL (v2.x, Go) is the only one of the three to have designed multi-tenancy as a first-class primitive. A single installation exposes independent organizations — each with its own users, external IdPs, password policies and applications. For an agency hosting ten clients on a single VPS, that is the difference between ten Keycloak configuration files and an API that orchestrates everything.",{"type":43,"title":44,"items":45},"ul","Why ZITADEL stands out for agency or B2B SaaS use",[46,47,48,49,50,51,52,53],"**Native multi-tenancy**: organizations and projects are data model primitives, not workarounds — each client gets an isolated space without configuration duplication","**Written in Go**: single binary, seconds to start, memory footprint of 150 to 300 MB full stack versus 512 MB to 1 GB of JVM for Keycloak","**OIDC, OAuth2, SAML 2.0 and LDAP**: all enterprise protocols in a single installation — no need to add extensions as with Keycloak","**Native Passkeys and WebAuthn from v2**: each organization can enforce Passkeys as the sole authentication factor, without plugins or advanced configuration","**API-first gRPC\u002FREST**: every operation available in the console is also available via the API — provision a tenant, create an OIDC application, or reset an MFA in a few lines of script","**Actions (webhooks\u002Fcode)**: custom logic triggered on authentication events (pre\u002Fpost-auth, post-register) without modifying the core","**Stateless application deployment**: ZITADEL stores everything in PostgreSQL — no distributed cache, no additional volume, horizontal scaling is as simple as starting a second instance","**Apache 2.0 licence**: the core is fully open source, with no critical features hidden behind an Enterprise licence",{"type":39,"title":55,"body":56},"Prerequisites: server, Docker and DNS","ZITADEL is reasonable on resources for an identity provider of this scope. The team-recommended minimum configuration for a production deployment is **2 vCPU and 4 GB of RAM** — of which about 300 MB will be used by ZITADEL itself, the rest going to PostgreSQL and other services in your stack.\n\nIn practice, on a dedicated VPS with 4 GB of RAM, ZITADEL + PostgreSQL leave plenty of room for an nginx reverse proxy, a notification service and a few applications. For testing or a development deployment, 2 GB is sufficient.\n\n**Required software:** Docker Engine 24+ and Docker Compose v2. Ports 80 and 443 must be open (nginx will terminate TLS). A domain name pointed at your VPS IP — ZITADEL requires a FQDN even in development, as OIDC cookies are bound to the domain. If you do not yet have a TLS certificate, Let's Encrypt with Certbot or the nginx plugin is sufficient.\n\n**Database:** PostgreSQL 15 or 16. ZITADEL also supports CockroachDB for highly available deployments, but on a single-node VPS, PostgreSQL is the natural choice. The database must be reachable from the ZITADEL container — the default Docker bridge network is fine.\n\n**Storage:** ZITADEL stores no files on disk (beyond logs) — no dedicated data volume beyond the PostgreSQL volume. Plan for 10 GB for the database under normal usage conditions.",{"type":58,"title":59,"steps":60},"steps","Deploy ZITADEL with Docker Compose and PostgreSQL",[61,64,67,70,73,76,79,82,85,88],{"title":62,"body":63},"Create the working directory tree","Connect to your VPS via SSH and create the necessary directories:\n```bash\nmkdir -p \u002Fopt\u002Fzitadel\u002F{config,certs}\ncd \u002Fopt\u002Fzitadel\n```\nAll configuration files and secrets will live here.",{"title":65,"body":66},"Create the .env file with secrets","Generate strong secrets before writing them to `.env`:\n```bash\ncat > \u002Fopt\u002Fzitadel\u002F.env \u003C\u003CEOF\nPOSTGRES_PASSWORD=$(openssl rand -base64 32 | tr -d '\\n')\nZITADEL_MASTERKEY=$(openssl rand -base64 32 | tr -d '\\n' | head -c 32)\nZITADEL_DOMAIN=sso.your-domain.com\nEOF\n```\nThe `MASTERKEY` must be exactly 32 characters — ZITADEL refuses to start without this.",{"title":68,"body":69},"Write the docker-compose.yml","Create `\u002Fopt\u002Fzitadel\u002Fdocker-compose.yml` with the PostgreSQL and ZITADEL services, configuring the database connection via environment variables and exposing port 8080 for nginx to proxy.",{"title":71,"body":72},"Configure nginx as TLS reverse proxy","Create an nginx server block for `sso.your-domain.com` that redirects HTTP to HTTPS and proxies requests to `http:\u002F\u002F127.0.0.1:8080`. Set the `X-Forwarded-Proto: https` header. Obtain the Let's Encrypt certificate with Certbot and reload nginx.",{"title":74,"body":75},"Start the ZITADEL stack","```bash\ncd \u002Fopt\u002Fzitadel\ndocker compose up -d\ndocker compose logs -f zitadel\n```\nThe first start (`start-from-init`) initializes the database, applies migrations and creates the default organization. Wait for `server is listening on [::]:8080` in the logs — this typically takes 30 to 60 seconds.",{"title":77,"body":78},"Retrieve initial admin credentials","During the first start with `start-from-init`, ZITADEL generates an admin account and a service key file. Retrieve them with:\n```bash\ndocker compose logs zitadel 2>&1 | grep -A 5 'initial'\n```\nCredentials are displayed only once in the logs. Note the login and password — they cannot be recovered after. If lost, restart with `start-from-init` after clearing the database.",{"title":80,"body":81},"Log in to the administration console","Open `https:\u002F\u002Fsso.your-domain.com\u002Fui\u002Fconsole` in a browser. Sign in with the credentials retrieved in the previous step. You arrive at the default organization dashboard — this is your main organization (your own account or agency). From here, you can create client organizations, projects and OIDC applications.",{"title":83,"body":84},"Create your first client organization","In the console, go to **Organization → New organization**. Give it your client's name (e.g. `client-acme`). ZITADEL automatically creates an isolated space: users in this organization cannot see those in others, and password policies can differ. You can then invite an organization admin for the client — they will have access only to their space.",{"title":86,"body":87},"Create a project and an OIDC application","In the organization, go to **Projects → New project**. A project groups one or more applications. Then create a **Web application** of type **OIDC**. ZITADEL asks for the client type (Web, SPA, Native) and redirect URLs. It generates a `Client ID` and `Client Secret`. Note the Discovery URL: `https:\u002F\u002Fsso.your-domain.com\u002F.well-known\u002Fopenid-configuration` — all OIDC-configured applications point to this endpoint.",{"title":89,"body":90},"Verify health and pin the image version","```bash\ncurl -s https:\u002F\u002Fsso.your-domain.com\u002Fhealthz | jq .\n```\nA `{\"status\": \"pass\"}` response confirms ZITADEL is responding correctly. For updates, replace the `stable` tag with the exact version number in `docker-compose.yml` and pin it — `stable` tracks the latest release, which can introduce unplanned changes in production.",{"type":39,"title":92,"body":93},"Configuring OIDC authentication for your applications","Once ZITADEL is running and an OIDC application is created, the client-side configuration is standard — any OIDC library (passport.js, next-auth, python-oidc, etc.) works without ZITADEL-specific adaptation.\n\nThe essential OIDC parameters to provide to your application:\n\n```\nISSUER=https:\u002F\u002Fsso.your-domain.com\nCLIENT_ID=\u003Cgenerated_client_id>\nCLIENT_SECRET=\u003Cgenerated_client_secret>  # only for Web apps (not SPAs)\nREDIRECT_URI=https:\u002F\u002Fyour-app.com\u002Fcallback\nSCOPES=openid profile email\n```\n\nThe Discovery Document is available at `https:\u002F\u002Fsso.your-domain.com\u002F.well-known\u002Fopenid-configuration`. It automatically exposes all endpoints: `authorization_endpoint`, `token_endpoint`, `userinfo_endpoint`, `jwks_uri`. Most modern OIDC libraries can configure themselves from this single discovery URL.\n\nFor SPAs (React, Vue), use the PKCE flow without a client-side secret. ZITADEL handles PKCE code rotation natively — no additional configuration required.",{"type":39,"title":95,"body":96},"Multi-tenant model: organizations, projects and applications","The ZITADEL data model is structured in three levels that precisely match the needs of an agency or B2B SaaS.\n\n**The organization** is a tenant container. Each organization has its own users, its own external IdPs (Google, GitHub, enterprise SAML) and its own password complexity policies. It can be administered by an organization delegate — your client — without giving them access to other organizations.\n\n**The project** groups the applications of a single product. A project can contain a web application, a mobile application and an API — all sharing the same roles and permissions, defined once at the project level. JWT tokens issued by ZITADEL carry project roles in the `urn:zitadel:iam:org:project:roles` claim.\n\n**The application** represents a specific OIDC, OAuth2 or SAML client. It defines redirect URLs, client type and allowed scopes.\n\nThis model allows an agency to manage ten clients on a single ZITADEL installation with zero risk of cross-tenant contamination — compare with Keycloak where each realm is a complete configuration to maintain separately.",{"type":98,"title":99,"headers":100,"rows":105},"comparison","ZITADEL vs Authentik vs Keycloak — technical comparison",[101,102,103,104],"Criterion","ZITADEL v2.x","Authentik 2026.8","Keycloak 26.7",[106,111,116,121,124,129,133,138],[107,108,109,110],"Language \u002F runtime","Go — single binary","Python + Rust (frontend)","Java (JVM)",[112,113,114,115],"Full stack RAM (idle)","150 – 300 MB","~1 GB (worker + PostgreSQL)","512 MB – 1 GB (JVM alone)",[117,118,119,120],"Native multi-tenancy","Yes — Organizations primitive","Partial — separate tenants","Partial — separate realms",[122,123,123,123],"OIDC \u002F OAuth2 \u002F SAML","Yes — all three native",[125,126,127,128],"Passkeys \u002F WebAuthn","Yes, native v2","Yes (2026.2+)","Yes (v21+)",[130,131,132,132],"API-first","gRPC + REST, 100% coverage","Partial REST",[134,135,136,137],"Admin interface","Web console + API","Full UI with visual flows","Full UI, XML configuration",[139,140,141,142],"Licence","Apache 2.0 (core)","MIT (Community) \u002F Enterprise","Apache 2.0",{"type":144,"title":145,"body":146},"tip","Enabling Passkeys and MFA in ZITADEL v2","ZITADEL v2 offers WebAuthn Passkeys as a primary authentication factor — not just as a second factor. To enable them in an organization:\n\n1. In the console, open **Organization → Security Policy → MFA Policy**.\n2. Enable **Passkeys** (passwordless WebAuthn) and select **Force** if you want all users in this organization to use only Passkeys.\n3. For a more flexible policy: allow both Passkeys and TOTP — the user chooses during enrollment.\n\nTo enforce MFA by default for all new users of a project:\n- Open the **Project → Settings → Force MFA** — this option requires every user of that project to register a second factor before accessing the application.\n\nPasskeys are stored client-side (in the browser or on a FIDO2 security key) and never transmitted to ZITADEL — only the cryptographic proof is exchanged. ZITADEL also supports standard TOTP (Google Authenticator, Bitwarden Authenticator, any RFC 6238-compatible app) and physical U2F\u002FFIDO2 security keys (YubiKey, etc.) for enterprise deployments.",{"type":39,"title":148,"body":149},"Troubleshooting: common ZITADEL Docker errors","**`masterkey must be 32 characters`**: the `ZITADEL_MASTERKEY` generated by `openssl rand -base64 32` produces a base64 string of variable length due to padding. Use `head -c 32` to truncate to exactly 32 characters, or use `openssl rand -hex 16` (32 hex characters = 16 bytes).\n\n**`connection refused` on port 8080 while the container is running**: ZITADEL only responds on `:8080` once migrations are complete — `start-from-init` on an empty database can take 30 to 90 seconds. Wait for the log `server is listening on [::]:8080` before testing.\n\n**HTTP\u002FHTTPS redirect loop**: ZITADEL generates its OAuth2 redirects in HTTPS when `ZITADEL_EXTERNALSECURE=true`. If nginx does not correctly send the `X-Forwarded-Proto: https` header, ZITADEL thinks it is behind HTTP and generates broken redirects. Verify that `proxy_set_header X-Forwarded-Proto https` is in nginx's `location \u002F` block.\n\n**`invalid_client` during OIDC flow**: verify that the redirect URL declared in the ZITADEL application matches exactly what your application sends — ZITADEL is strict about matching (no trailing slash, no extra parameters).\n\n**PostgreSQL connection refused**: the ZITADEL container starts before PostgreSQL is ready to accept connections, even with `depends_on: condition: service_healthy`. If this occurs, simply restart ZITADEL: `docker compose restart zitadel`.\n\n**Lost initial admin credentials**: credentials are displayed only once at first startup. If lost, remove the PostgreSQL database (`docker compose down -v`) and restart `start-from-init`. In production, immediately create a second admin account and store credentials in your secrets manager.",{"type":39,"title":151,"body":152},"Next step: deepen your self-hosted SSO stack","ZITADEL in production on a VPS is an excellent first step. To go further:\n\n- **[Deploy ZITADEL on VPS — advanced guide](\u002Fdeployer-zitadel-vps)**: automatic TLS certificates with Let's Encrypt, PostgreSQL backup with pg_dump to S3, and zero-downtime updates.\n- **[Authentik vs Authelia: which self-hosted SSO for your VPS?](\u002Fauthentik-vs-authelia-sso-self-hosted)**: if your use case is less multi-tenant-oriented and more authentication proxy-oriented, the comparison of the two lightest alternatives.\n- **[Migrating from Keycloak to Authelia or ZITADEL](\u002Fkeycloak-cves-semaine-38-migrer-authelia-zitadel-vps)**: the six week-38 CVEs analyzed, and the concrete migration path from an existing Keycloak realm.\n- **[Self-hosting Authelia on VPS](\u002Fself-host-authelia-vps)**: if you are looking for an lightweightweight authentication gateway (less than 30 MB) rather than a full IdP, Authelia remains the reference.\n\nA VPS with 4 GB of RAM, root access and a dedicated IPv4 comfortably covers ZITADEL in production with room to spare for the applications it protects.","Voir les VPS Cloud","\u002Fvps-cloud",[156,178,200,218],{"id":157,"slug":158,"slugs":159,"title":163,"excerpt":164,"readTime":165,"views":18,"isPinned":19,"publishedAt":166,"updatedAt":167,"category":168,"categories":174,"featuredImage":30,"bgImage":31,"posterImage":176,"relatedSolution":177},351,"self-host-zitadel-vps-open-source-iam-developers",{"fr":160,"en":158,"ar":161,"es":162},"deployer-zitadel-vps","zitadel-vps-iam-maftouh-almassdar-lilmutatawireen","instalar-zitadel-vps-iam-codigo-abierto-desarrolladores","Self-Host ZITADEL on a VPS: Open-Source IAM for Developers","Deploy ZITADEL self-hosted on a VPS: OIDC, OAuth 2.0, passkeys and native multi-tenancy with Docker Compose in under 5 minutes.",3,"2026-01-08T00:00:00+00:00","2026-09-16T14:44:13+00:00",{"id":169,"name":170,"slug":171,"color":172,"icon":173},7,"Self-hosting","self-hosting","bg-indigo-500\u002F10 text-indigo-400","cloud",[175],{"id":169,"name":170,"slug":171,"color":172,"icon":173},"\u002Fblog\u002Fcovers\u002Fdeployer-zitadel-vps-poster.svg",{"categorySlug":27,"appSlug":35},{"id":179,"slug":180,"slugs":181,"title":185,"excerpt":186,"readTime":187,"views":18,"isPinned":19,"publishedAt":188,"updatedAt":189,"category":190,"categories":195,"featuredImage":30,"bgImage":31,"posterImage":197,"relatedSolution":198},423,"authentik-vs-authelia-sso-comparison-vps",{"fr":182,"en":180,"ar":183,"es":184},"authentik-vs-authelia-sso-self-hosted","authentik-مقابل-authelia-sso-مستضاف-ذاتيا-vps","authentik-vs-authelia-sso-comparativa-vps","Authentik vs Authelia: Which Self-Hosted SSO for Your VPS?","Authentik or Authelia for your self-hosted SSO? Technical comparison: OIDC\u002FSAML protocols, RAM footprint, architecture and use cases to help you decide.",9,"2026-10-08T00:00:00+00:00","2026-10-08T23:30:51+00:00",{"id":191,"name":192,"slug":193,"color":194,"icon":193},5,"Comparison","comparatif","bg-info\u002F10 text-info",[196],{"id":191,"name":192,"slug":193,"color":194,"icon":193},"\u002Fblog\u002Fcovers\u002Fauthentik-vs-authelia-sso-self-hosted-poster.svg",{"categorySlug":34,"appSlug":199},"authentik",{"id":201,"slug":202,"slugs":203,"title":207,"excerpt":208,"readTime":209,"views":18,"isPinned":19,"publishedAt":210,"updatedAt":211,"category":212,"categories":213,"featuredImage":30,"bgImage":31,"posterImage":215,"relatedSolution":216},372,"keycloak-cves-week-38-migrate-authelia-zitadel-vps",{"fr":204,"en":202,"ar":205,"es":206},"keycloak-cves-semaine-38-migrer-authelia-zitadel-vps","ثغرات-keycloak-الأسبوع-38-هجرة-إلى-authelia-zitadel","keycloak-cves-semana-38-migrar-authelia-zitadel-vps","Keycloak v26.7.4: 6 CVEs — migrate to Authelia or ZITADEL","Keycloak v26.7.4 patches 6 CVEs including two unauthenticated DoS. For fewer than 10 SSO apps on a VPS, Authelia (\u003C 30 MB) or ZITADEL cut the attack surface.",12,"2026-09-23T00:00:00+00:00","2026-09-23T15:23:17+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[214],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fkeycloak-cves-semaine-38-migrer-authelia-zitadel-vps-poster.svg",{"categorySlug":34,"appSlug":217},"authelia",{"id":219,"slug":220,"slugs":221,"title":225,"excerpt":226,"readTime":227,"views":165,"isPinned":19,"publishedAt":228,"updatedAt":229,"category":230,"categories":231,"featuredImage":30,"bgImage":31,"posterImage":233,"relatedSolution":234},162,"self-host-authelia-on-a-vps-mfa-and-sso-for-your-whole-stack",{"fr":222,"en":220,"ar":223,"es":224},"self-host-authelia-vps","استضافة-authelia-على-vps-مصادقة-ثنائية-ودخول-موحد-لمنظومتك","alojar-authelia-en-un-vps","Self-hosting Authelia on a VPS: MFA and SSO for your entire stack","Deploy Authelia on a VPS with Docker Compose: TOTP, WebAuthn\u002Fpasskey, OIDC, LLDAP migration, Prometheus monitoring, and comparison with Authentik.",10,"2026-07-04T00:00:00+00:00","2026-09-24T12:27:51+00:00",{"id":23,"name":24,"slug":25,"color":26,"icon":27},[232],{"id":23,"name":24,"slug":25,"color":26,"icon":27},"\u002Fblog\u002Fcovers\u002Fself-host-authelia-vps-poster.svg",{"categorySlug":27,"appSlug":217},1791901056181]