← Back to posts
comparison guide self-hosted · · 13 min read

Casdoor vs Zitadel vs Authentik: Lightweight Self-Hosted SSO Guide 2026

Compare three modern self-hosted identity providers — Casdoor, Zitadel, and Authentik. Docker deployment guides, protocol support comparison, and which SSO solution fits your infrastructure in 2026.

OS
Editorial Team

Why Self-Host Your Single Sign-On Provider?

Authentication is the foundation of every service you run. Whether you manage a homelab with a dozen applications or operate a multi-team infrastructure with hundreds of services, users need a reliable, secure way to log in once and access everything. Cloud-hosted SSO providers — Okta, Auth0, Microsoft Entra ID — charge per active user, impose rate limits, and store your user data on their servers. For organizations handling sensitive data or operating under data residency regulations (GDPR, HIPAA, SOC 2), handing authentication to a third party introduces compliance risk and ongoing cost.

Self-hosting a single sign-on provider gives you full control over the authentication flow, user directory, session policies, and audit logs. You eliminate per-user licensing fees, avoid vendor lock-in, and can integrate with any internal system — LDAP directories, HR databases, custom applications — without waiting for a vendor connector. The self-hosted identity landscape in 2026 has matured significantly, with several projects offering enterprise-grade features alongside simple docker-based deployments.

This guide compares three modern, actively maintained self-hosted identity providers: Casdoor, Zitadel, and Authentik. Each takes a different approach to the problem, from Go-based simplicity to event-sourced multi-tenancy to Django-powered extensibility. We cover architecture, protocol support, deployment complexity, and provide production-ready Docker Compose configurations for each.


The Contenders at a Glance

FeatureCasdoorZitadelAuthentik
Language / StackGo + React frontendGoPython (Django) + Go workers
GitHub Stars13,400+13,500+21,000+
DatabaseMySQL, PostgreSQL, SQLite, SQL Server, OracleCockroachDB, PostgreSQLPostgreSQL
LicenseApache 2.0Apache 2.0 (core)Mozilla Public License 2.0
OIDC / OAuth2Full support (all standard flows)Full support (all flows)Full support (all flows)
SAML 2.0Yes (IdP and SP)Yes (IdP and SP)Yes (IdP and SP)
LDAPYes (IdP)No (read-only via external IdP)Yes (full IdP + proxy)
SCIMYes (v2)Yes (preview)Yes (v2)
Social Login40+ providers (Google, GitHub, Discord, WeChat, etc.)Google, Microsoft, Apple, GitHub, GitLab, custom OIDC/SAML30+ providers (Google, Microsoft, Discord, GitHub, etc.)
Multi-Factor AuthTOTP, WebAuthn, SMS, Email, Face IDTOTP, WebAuthn, U2F, Email, SMSTOTP, WebAuthn, Duo, static TOTP
Multi-TenancyYes (organizations)Yes (instances with full data isolation)Yes (separate instances via config)
APIREST + OpenAPIgRPC + RESTREST
Admin UIReact single-page appZITADEL Console (modern SPA)Django-based SPA
Deployment FootprintSingle binary + databaseSingle binary + CockroachDB/PostgreSQL2-3 containers (server, worker, PostgreSQL)
Best ForTeams wanting a lightweight, easy-to-deploy IdP with broad social login supportMulti-tenant SaaS platforms needing strong data isolation and audit trailsOrganizations wanting a highly extensible auth system with rich policy engine

Architecture and Design Philosophy

Casdoor: Simplicity First

Casdoor is built in Go with a React frontend. Its design philosophy centers on simplicity — a single binary serves the admin UI, API, and authentication endpoints. The project was created by the Casbin team and shares the same pragmatic approach: get the core functionality right, support many protocols and providers, and keep deployment friction minimal.

The architecture is straightforward: Casdoor connects to a relational database (MySQL, PostgreSQL, SQLite, or others) and exposes REST APIs for identity management. There is no message queue, no worker process, no distributed event store. This means Casdoor scales vertically rather than horizontally — you run one instance behind a load balancer with a replicated database for high availability. For most homelab and small-to-medium team deployments, this is more than sufficient.

Zitadel: Event-Sourced Multi-Tenancy

Zitadel takes a fundamentally different approach. Every action — user creation, password change, login attempt — is stored as an immutable event in an event store. This event-sourced architecture means Zitadel can reconstruct the full history of any entity, supports temporal queries (“what permissions did this user have on March 15?”), and provides audit compliance out of the box without additional logging infrastructure.

Zitadel’s multi-tenancy model is built from the ground up. Each “instance” (tenant) gets its own set of organizations, users, projects, and applications with complete data isolation. This makes Zitadel particularly well-suited for B2B SaaS platforms that need to onboard multiple customer organizations with separate identity domains.

The trade-off is operational complexity. Zitadel requires CockroachDB (or PostgreSQL with limitations) as its database, and the event store grows continuously — you need to understand snapshotting and event retention policies for long-running deployments.

Authentik: The Extensible Auth Engine

Authentik combines a Python/Django backend with Go-based workers for heavy lifting (certificate generation, email sending, LDAP sync). Its design philosophy is extensibility — every authentication step, policy decision, and flow can be customized through the admin UI or API. The policy engine is particularly powerful, allowing administrators to chain conditions like “user must be in group X AND connecting from trusted IP AND have active WebAuthn device” before granting access.

Authentik’s architecture separates the server (Django, handling the admin UI and API) from the worker (Go, processing asynchronous tasks like email delivery and certificate rotation). Both components share a PostgreSQL database. This separation means Authentik can handle high-throughput authentication flows without blocking the admin interface, but it also means one more container to manage in your Docker Compose stack.


Deployment Guides

Deploying Casdoor with Docker Compose

Casdoor’s deployment is the simplest of the three — a single service connected to a MySQL database. The official Docker Compose configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
services:
  casdoor:
    image: casbin/casdoor:latest
    restart: always
    ports:
      - "8000:8000"
    depends_on:
      db:
        condition: service_healthy
    environment:
      RUNNING_IN_DOCKER: "true"
    volumes:
      - ./conf:/conf/
    command: ["--createDatabase=true"]

  db:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: casdoor-secret
      MYSQL_DATABASE: casdoor
    volumes:
      - casdoor-db:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  casdoor-db:

Start with docker compose up -d and access the admin UI at http://localhost:8000. The default credentials are admin / 123. Casdoor auto-creates the database schema on first run thanks to the --createDatabase=true flag.

The configuration directory (./conf) holds app.conf where you set the database connection string, session settings, and SMTP configuration for email-based workflows. For production, replace the MySQL root password and add a reverse proxy with TLS termination.

Deploying Zitadel with Docker Compose

Zitadel’s official self-hosting setup uses CockroachDB for its event store. Here is a production-oriented compose configuration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
services:
  zitadel:
    image: ghcr.io/zitadel/zitadel:latest
    restart: unless-stopped
    command: 'start-from-init --masterkey "x12345678901234567890123456789012" --tlsMode disabled'
    ports:
      - "8080:8080"
    depends_on:
      cockroachdb:
        condition: service_healthy
    environment:
      ZITADEL_DATABASE_COCKROACH_HOST: cockroachdb
      ZITADEL_DATABASE_COCKROACH_PORT: 26257
      ZITADEL_DATABASE_COCKROACH_DATABASE: zitadel
      ZITADEL_DATABASE_COCKROACH_USER_USERNAME: zitadel
      ZITADEL_DATABASE_COCKROACH_USER_SSL_MODE: disable
      ZITADEL_EXTERNALSECURE: false
      ZITADEL_MASTERKEY: x12345678901234567890123456789012

  cockroachdb:
    image: cockroachdb/cockroach:latest
    restart: unless-stopped
    command: 'start-single-node --insecure --store=attrs=ssd,path=/var/lib/cockroach/data'
    ports:
      - "26257:26257"
    healthcheck:
      test: ["CMD", "cockroach", "sql", "--insecure", "--execute=SELECT 1"]
      interval: 10s
      timeout: 30s
      retries: 5
      start_period: 20s
    volumes:
      - cockroach-data:/var/lib/cockroach/data

volumes:
  cockroach-data:

Key configuration notes:

  • The masterkey must be a 32-character string — Zitadel uses it to encrypt sensitive data at rest. Generate one with openssl rand -base64 32.
  • --tlsMode disabled and ZITADEL_EXTERNALSECURE: false are for local deployment behind a reverse proxy. Remove these for direct TLS.
  • The start-from-init command handles database initialization, schema migration, and startup in one step.

After starting, Zitadel’s console is available at http://localhost:8080/ui/console. The first-run wizard guides you through creating the admin user and instance.

Deploying Authentik with Docker Compose

Authentik requires three services: PostgreSQL, the main server, and a background worker:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
services:
  postgresql:
    image: docker.io/library/postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: authentik
      POSTGRES_USER: authentik
      POSTGRES_PASSWORD: authentik-secure-password
    volumes:
      - authentik-db:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d authentik -U authentik"]
      interval: 30s
      timeout: 5s
      retries: 5

  server:
    image: ghcr.io/goauthentik/server:2026.2.2
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_SECRET_KEY: your-secret-key-change-this
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: authentik-secure-password
    ports:
      - "9000:9000"
      - "9443:9443"
    volumes:
      - ./authentik-data:/data
      - ./custom-templates:/templates
    depends_on:
      postgresql:
        condition: service_healthy

  worker:
    image: ghcr.io/goauthentik/server:2026.2.2
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_SECRET_KEY: your-secret-key-change-this
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_POSTGRESQL__USER: authentik
      AUTHENTIK_POSTGRESQL__PASSWORD: authentik-secure-password
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./authentik-data:/data
      - ./certs:/certs
      - ./custom-templates:/templates
    depends_on:
      postgresql:
        condition: service_healthy

volumes:
  authentik-db:

Generate the AUTHENTIK_SECRET_KEY with python3 -c "from secrets import token_urlsafe; print(token_urlsafe(50))". The worker mounts the Docker socket to enable container-based authentication flows — this is optional and can be removed if you do not need Docker integration.

The server listens on port 9000 (HTTP) and 9443 (HTTPS). After startup, the admin interface is at http://localhost:9000/if/flow/initial-setup/ for the first-run configuration.


Protocol and Integration Comparison

OIDC / OAuth2 Support

All three projects support the full OpenID Connect specification, including Authorization Code flow (with PKCE), Client Credentials, Device Code, and Refresh Token flows. The differences emerge in implementation details:

  • Casdoor provides a straightforward OIDC implementation with a clean discovery document at /.well-known/openid-configuration. Token signing supports RS256 and ES256.
  • Zitadel offers the most complete OIDC feature set, including JWT profile grants, device authorization, and CIBA (Client Initiated Backchannel Authentication). Its token format includes comprehensive claims for audit purposes.
  • Authentik supports all standard OIDC flows and adds a unique “flows and stages” system that lets you customize the authentication journey — insert additional verification steps, conditional MFA, or custom logic between the username/password prompt and token issuance.

SAML 2.0 Integration

For legacy application integration or enterprise SSO federation, SAML 2.0 support matters:

  • Casdoor acts as both SAML IdP and SP, with XML metadata export for easy partner configuration. It supports SAML 2.0 Web Browser SSO profile with POST and Redirect bindings.
  • Zitadel provides SAML IdP and SP capabilities with full attribute mapping and NameID format configuration. Enterprise features include SAML assertion encryption and artifact resolution.
  • Authentik supports SAML with a provider configuration that maps attributes, configures assertion lifetimes, and handles encryption. The SAML provider integrates with Authentik’s policy engine, so you can apply access conditions to SAML assertions.

LDAP and Directory Integration

For organizations with existing Active Directory or OpenLDAP directories:

  • Casdoor can import users from LDAP and use it as an external authentication source. The LDAP connector supports standard attribute mapping.
  • Zitadel does not natively act as an LDAP server. It can integrate with external identity providers via OIDC or SAML, but does not provide LDAP read or write capabilities.
  • Authentik excels here — it provides a full LDAP proxy that translates LDAP bind requests into Authentik authentication flows, plus an LDAP outbound sync that pushes users to external LDAP directories. For Active Directory integration, Authentik also supports Kerberos authentication.

Which One Should You Choose?

Choose Casdoor If

You want the simplest possible deployment with the broadest social login support. Casdoor’s single-binary architecture means one container to manage, and its 40+ social login providers cover virtually every major platform. It is the best fit for:

  • Small teams and homelab users who want SSO without operational overhead
  • Applications targeting Asian markets (WeChat, QQ, DingTalk support built-in)
  • Projects already using Casbin for authorization (native integration)

Choose Zitadel If

You operate a multi-tenant platform where customer data isolation is non-negotiable. Zitadel’s event-sourced architecture provides audit trails, temporal queries, and complete tenant isolation out of the box. It is the best fit for:

  • B2B SaaS platforms serving multiple organizations
  • Companies with strict audit and compliance requirements
  • Teams that value API-first design (gRPC + REST with generated clients)

Choose Authentik If

You need deep customization of the authentication flow and strong LDAP/Active Directory integration. Authentik’s policy engine and flow system give you fine-grained control over every step of the authentication process. It is the best fit for:

  • Organizations migrating from Active Directory or OpenLDAP
  • Teams that need conditional authentication (location-based, device-based, risk-based MFA)
  • Environments where authentication policies must adapt to context (IP, time, user attributes)

Performance and Resource Requirements

MetricCasdoorZitadelAuthentik
Minimum RAM256 MB512 MB1 GB
Database RAM512 MB (MySQL)2 GB (CockroachDB)512 MB (PostgreSQL)
Startup Time~2 seconds~10 seconds (with init)~15 seconds
Disk (base)~100 MB~200 MB~400 MB
Disk (30-day growth, 10K users)~200 MB~2-4 GB (event store)~500 MB
Auth requests/sec (single node)~5,000~3,000~2,000

Casdoor has the lightest footprint — a single Go binary with minimal dependencies. Zitadel’s CockroachDB requirement adds significant resource overhead, but this is the price of event-sourced durability and horizontal scalability. Authentik sits in the middle, with reasonable resource usage for its feature set.


Security Considerations

All three projects support modern security practices:

  • Password hashing: Casdoor uses bcrypt, Zitadel uses bcrypt with configurable cost, Authentik uses Argon2id (the current recommended algorithm for password storage).
  • Session management: All support configurable session lifetimes, refresh token rotation, and revocation.
  • CSRF and XSS protection: Built-in across all three admin consoles.
  • Rate limiting: Casdoor has configurable rate limits per endpoint, Zitadel has built-in login attempt throttling, Authentik includes brute force protection with configurable lockout policies.
  • Audit logging: Zitadel’s event store provides the most comprehensive audit trail by design. Authentik logs all authentication events with full context. Casdoor provides basic operation logs that can be exported to external systems.

For production deployments, always place these services behind a reverse proxy (such as Traefik, Caddy, or Nginx) with TLS termination, and configure proper CORS policies for your application domains. If you need to protect applications that do not natively support OIDC, consider using an OAuth2 proxy like Pomerium or Traefik Forward Auth as a sidecar to enforce authentication at the network level.

For authorization — the complement to authentication — check our guide on self-hosted authorization engines like Casbin, OPA, and Cedar. Since Casdoor is built by the Casbin team, the two integrate natively for fine-grained access control on top of SSO.


FAQ

Can Casdoor replace Auth0 or Okta for production use?

Yes, for many use cases. Casdoor supports all standard OIDC and SAML flows, provides social login with 40+ providers, and handles user management, MFA, and session management. The main limitations compared to Auth0 or Okta are the lack of a global CDN, built-in anomaly detection, and enterprise-grade SLAs. For self-hosted deployments where you control the infrastructure, Casdoor provides the core identity features without per-user licensing costs.

Does Zitadel work with PostgreSQL instead of CockroachDB?

Yes, Zitadel supports PostgreSQL as an alternative database. However, some features like distributed multi-node deployment and certain event-sourcing optimizations are designed around CockroachDB. For single-node deployments, PostgreSQL works well and reduces the operational footprint significantly. Use the ZITADEL_DATABASE_POSTGRES_* environment variables instead of the CockroachDB equivalents.

How does Authentik handle high availability?

Authentik supports horizontal scaling by running multiple server instances behind a load balancer, all sharing the same PostgreSQL database. The worker processes can also be scaled independently. For the database layer, PostgreSQL streaming replication with a read replica handles read scaling, and tools like Patroni or Stolon provide automated failover. Authentik’s stateless server design means any instance cankeycloakany request.

Can I migrate users from Keycloak to any of these platforms?

Yes, all three support user import from external sources. Casdoor and Authentik can import users via CSV upload or LDAP sync. Zitadel provides a bulk user import API. For direct Keycloak migration, you can export users from Keycloak’s PostgreSQL database and import them using the target platform’s user creation API — password hashes can be migrated if both systems support the same hashing algorithm (typically bcrypt or PBKDF2).

Which solution has the best mobile app integration?

All three support mobile authentication through OIDC’s Authorization Code flow with PKCE, which is the recommended approach for native mobile apps. Zitadel provides official SDKs for Flutter, React Native, and Swift/Kotlin, making integration slightly easier for mobile development teams. Casdoor and Authentik work with any OIDC-compatible mobile SDK (such as AppAuth for iOS/Android) but do not provide platform-specific libraries.

Is SSO federation (connecting multiple identity providers) supported?

Yes. Casdoor can chain multiple social and enterprise identity providers as upstream authentication sources. Zitadel supports OIDC and SAML IdP connections, allowing you to federate authentication from external providers into your Zitadel instance. Authentik has the most flexible federation model through its “sources” system, which can connect OIDC, SAML, LDAP, and custom authentication sources, each with their own policy conditions.


Advertise here