
Identity
Keycloak vs Authentik vs Zitadel: Self-Hosted SSO After Okta
Own your identity plane: a trust-model decision matrix comparing Keycloak, Authentik, and Zitadel as self-hosted SSO — the engineering exit from Okta.
Most engineering organizations can tell you exactly where their production data lives and who can compel access to it. Ask the same question about the *login* — the single front door every employee and customer passes through to reach anything — and the answer is usually a vendor's name. Identity is the most load-bearing dependency in the building: it decides who is allowed to do what, and it has quietly become the one component almost nobody owns. The plane is rented, the bill is metered per human, and the blast radius of its failure is every system behind it.
This is not an anti-Okta polemic — Okta and Auth0 remain excellent for many teams. But "correct for now" and "something you could never leave" are different postures, and the distance is the optionality this practice defends. This article is the concrete exit for the human identity plane: owned, open-source providers — Keycloak, Authentik, and Zitadel — chosen by trust model, wired into an SSO layer you operate, audit, and can replace one protocol at a time.
It is deliberately the human-identity complement to our work on machine identity. The SPIFFE/SPIRE workload identity playbook is about how *services* prove who they are to each other — attested, secret-free, cryptographic identity for code. This piece is about how *people* prove who they are to your applications: sessions, multi-factor, consent, federation, and the deprovisioning that has to happen the hour someone leaves. The two planes meet, but they are governed by different tools and different failure modes, and conflating them is how identity programs go wrong.
The Year Identity Got a Price Tag
The case for owning identity used to be mostly philosophical. Three forces in the last few years turned it operational: the economics, the breaches, and the jurisdiction. None is a reason to panic. Together they are a reason to have an exit ramp — a different and more durable thing.
The economics came first. Okta acquired Auth0 in 2021 for $6.5 billion, and customer identity is billed per monthly active user — every unique human who authenticates at least once in a billing period. That meter has a direction. In its November 2023 pricing change, Auth0's B2C overage rate moved from $0.023 to $0.07 per MAU — roughly threefold — while base tiers were re-cut to fewer included users. Teams report renewal quotes several times their prior bills as users grow, and capabilities a serious deployment needs — SAML, advanced MFA, lifecycle governance — sit behind higher tiers. The invoice is set by someone else's strategy, on their schedule.
Then jurisdiction. In July 2020 the EU Court of Justice invalidated the Privacy Shield in *Schrems II*, on the grounds that US surveillance law is incompatible with EU data-protection guarantees. Its successor, the EU-US Data Privacy Framework, is itself under live legal challenge — the General Court dismissed the first case in September 2025, and an appeal was lodged with the Court of Justice on 31 October 2025 (Case C-703/25 P), still pending. For a European company, routing every employee and customer authentication — and the personal data inside those tokens — through a US-headquartered identity vendor is now a standing compliance question, not a settled one. That pressure is why "European alternatives to Okta" became its own procurement category in 2026, and it stacks directly on the NIS2 and DORA obligations that name identity providers as critical dependencies an audited entity must be able to control and evidence. The same logical-access criterion shows up outside the EU: SOC 2's CC6 maps directly onto OIDC and RBAC, and a self-hosted IdP's own audit log answers it more directly than a vendor's carve-out report ever could.
Human Identity Is Not Machine Identity
Before choosing a tool, be precise about which problem it solves, because the identity word covers two planes that look similar and behave nothing alike. Workload identity is how a service proves itself to another: a SPIFFE SVID issued after node and workload attestation, short-lived, auto-rotated, with no human in the loop and no password anywhere — the SPIFFE/SPIRE plane, where SPIRE, mTLS, and X.509 are the right tools.
Human identity is a different animal. A person authenticates interactively, holds a session that outlives a single request, carries factors that can be phished and so need MFA or passkeys, grants consent, and — critically — must be *deprovisioned* the moment they leave, across every downstream app at once. The protocol surface is OIDC, OAuth 2.0, SAML, and SCIM, not SVIDs and node attestation. Keycloak, Authentik, and Zitadel live entirely on this human plane; none replaces SPIRE, and SPIRE does not replace them. The mature posture runs both — cryptographic identity for workloads, sovereign SSO for people — with a clear seam where a human's token authorizes a workload action. Pick the wrong plane's tooling and you spend a quarter discovering it.
What an Identity Provider Actually Owns
An identity provider is small in concept and enormous in consequence. It owns the user store — the directory of accounts, organized as realms, organizations, or tenants. It owns the session and tokens minted from it, the factors that gate them (TOTP, WebAuthn/passkeys, recovery flows), the federation to upstream sources (Active Directory, Google, Microsoft Entra), and the provisioning that creates and destroys accounts downstream. Lose control of any of these and you have lost control of who can do what.
Owning it is even feasible only because every seam is a standard protocol. OIDC is an authentication layer on top of OAuth 2.0 — OAuth alone authorizes access and says nothing about *who* the user is; OIDC adds the identity in a signed ID token and a discovery document at a well-known URL. SAML 2.0 is the older XML protocol enterprise and legacy apps still speak; SCIM (RFC 7643/7644) provisions and de-provisions accounts; and the emerging OAuth 2.1 consolidation makes PKCE mandatory and drops the implicit flow. Because these contracts are open, an app consuming standard OIDC neither knows nor cares which IdP issued its token — which is what makes the provider behind the seam replaceable.
Keycloak: The Incumbent Standard
Keycloak is the default answer for a reason: open since 2013, it speaks essentially everything, and in April 2023 it joined the CNCF as an incubating project, giving it neutral governance to match its reach. The modern distribution is a real change from the Keycloak many teams remember: the old WildFly server is gone — the Quarkus distribution became default in Keycloak 17 and WildFly was removed entirely in Keycloak 20 — so the 26.x line starts faster, uses less memory, and behaves like a container-native service. Red Hat ships a supported build (RHBK) for teams wanting a vendor SLA behind the same codebase.
Architecturally, Keycloak organizes the world into realms: each realm is an isolated universe of users, clients, roles, and identity-brokering config, administered from a master realm. It requires an external relational database — PostgreSQL is the recommended store, with no production-grade embedded option — and it clusters for high availability, with database-based node discovery (JDBC-Ping) the default since 26.1, removing the old reliance on UDP multicast. An official Kubernetes operator manages the deployment. The trade is honest: you get the broadest protocol and brokering support of the three and a colossal community, at the cost of real operational weight — a database to run, a cluster to coordinate, and a configuration surface deep enough to need a specialist. The realm model is also an awkward fit for true B2B multi-tenancy, where each customer wants its own isolated identity space.
# Keycloak 26 (Quarkus): register an OIDC client for a downstream app.
# kcadm talks to the admin REST API — the same surface the operator drives.
kcadm.sh config credentials \
--server https://idp.example.internal \
--realm master --user "$ADMIN" --password "$ADMIN_PW"
# Create a realm dedicated to the workforce, then a confidential OIDC client.
kcadm.sh create realms -s realm=workforce -s enabled=true
kcadm.sh create clients -r workforce \
-s clientId=internal-dashboard \
-s protocol=openid-connect \
-s publicClient=false \
-s standardFlowEnabled=true \
-s 'redirectUris=["https://dashboard.example.internal/oauth2/callback"]' \
-s 'attributes={"pkce.code.challenge.method":"S256"}'
# The app now trusts one issuer you operate:
# https://idp.example.internal/realms/workforce/.well-known/openid-configurationAuthentik: The Application-Centric Challenger
Authentik, built by the public-benefit company Authentik Security, approaches the problem from the application side rather than the protocol side, and that reframing is its whole appeal. Its core is a Django application; its distinctive move is a fleet of stateless Go-based **outposts** — a proxy outpost that does forward authentication for apps that speak no SSO at all, an LDAP outpost for legacy LDAP clients, and a RADIUS outpost for network gear. The proxy outpost is the headline: it puts a real authentication gate in front of an internal tool that was never designed for one — the exact situation most organizations are actually in.
Authentik models authentication as flows composed of stages — a directed graph where each stage (identify, password, TOTP, WebAuthn, consent, enrollment, recovery) is a unit you can reorder, branch, or customize, in a visual editor or as YAML blueprints you commit to git. It supports OIDC, SAML, LDAP, RADIUS, and SCIM provisioning, and has been steadily closing enterprise gaps: single logout arrived in 2025.10, passkey autofill (WebAuthn conditional UI) in 2025.12. Operationally it has been getting *lighter* — the 2025.10 release removed the Redis dependency, folding caching and task queues into PostgreSQL, so a production deployment is now Authentik plus one database. It is the natural choice when your reality is a sprawl of heterogeneous apps — some modern, many not — that all need to sit behind one login.
# Authentik's proxy outpost gating an app that speaks no SSO.
# nginx forwards every request to the outpost's /auth/nginx endpoint;
# only an authenticated session is allowed through to the upstream.
location / {
auth_request /outpost.goauthentik.io/auth/nginx;
error_page 401 = @goauthentik_proxy_signin;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
# Pass identity headers from the outpost to the protected app.
auth_request_set $authentik_username $upstream_http_x_authentik_username;
proxy_set_header X-authentik-username $authentik_username;
proxy_pass http://legacy-internal-tool:8080;
}
location /outpost.goauthentik.io {
proxy_pass http://authentik-proxy-outpost:9000/outpost.goauthentik.io;
proxy_set_header Host $host;
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
auth_request_set $auth_cookie $upstream_http_set_cookie;
add_header Set-Cookie $auth_cookie;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
}
location @goauthentik_proxy_signin {
internal;
add_header Set-Cookie $auth_cookie;
return 302 /outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}Zitadel: Multi-Tenant and API-First
Zitadel, from the Swiss company CAOS in St. Gallen, is built for a specific shape of problem: multi-tenant SaaS that needs identity as programmable infrastructure. Its architecture is event-sourced — every change to identity state is stored as an immutable event, so the complete, replayable history of who changed what and when is a property of the design, not a logging feature you bolt on — and for a regulated business that has to *prove* its access controls, an audit trail that cannot be silently edited is a meaningful primitive. Tenancy is first-class: Organizations are isolated tenants, each with its own users, branding, and IdP config — the model B2B platforms actually need, and the one Keycloak's realms approximate more awkwardly.
Zitadel is API-first in the literal sense — every capability is exposed over gRPC, connectRPC, and REST, so identity is managed as code rather than clicked together in a console — and passkeys/FIDO2 are first-class. One nuance matters for a sovereignty decision and deserves to be stated plainly: with the v3 release in March 2025 (current line: v4, GA July 2025), Zitadel changed its main repository license from Apache 2.0 to AGPL v3.0 and dropped CockroachDB for PostgreSQL only. The license move is a deliberate copyleft stance — modifications offered as a service must be shared back — and it was not universally popular with downstream adopters, exactly the kind of governance fact you weigh before standing on a project for a decade. Self-hosted, in Swiss jurisdiction, with an immutable event log, Zitadel is a strong answer for API-driven, multi-tenant identity where data residency is a hard requirement.
# Zitadel is API-first: provision identity over its management API, not a console.
# Create a project, then an OIDC application, with a service-account token.
curl -s -X POST \
https://idp.example.internal/management/v1/projects \
-H "Authorization: Bearer $ZITADEL_PAT" \
-H "Content-Type: application/json" \
-d '{ "name": "customer-portal" }'
curl -s -X POST \
"https://idp.example.internal/management/v1/projects/$PROJECT_ID/apps/oidc" \
-H "Authorization: Bearer $ZITADEL_PAT" \
-H "Content-Type: application/json" \
-d '{
"name": "portal-web",
"redirectUris": ["https://portal.example.internal/callback"],
"responseTypes": ["OIDC_RESPONSE_TYPE_CODE"],
"grantTypes": ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE"],
"authMethodType": "OIDC_AUTH_METHOD_TYPE_NONE",
"appType": "OIDC_APP_TYPE_USER_AGENT"
}'The Trust-Model Decision Matrix
Choosing among the three is not a feature beauty contest, because on the core protocols they substantially overlap — all three do OIDC, OAuth 2.0, SAML 2.0, and passkeys. The useful question is which *constraint* dominates your context. Need the widest protocol and identity-brokering surface, deep SAML, and a vendor-supported build? Keycloak is the incumbent standard and the safe institutional choice. Is your real problem a heterogeneous estate of applications — many speaking no SSO — that all need to sit behind one gate? Authentik's forward-auth outposts and flow model are purpose-built for it. Building multi-tenant SaaS, managing identity as code through an API, needing a tamper-evident audit trail? Zitadel's organizations and event sourcing fit best.
Migrating Off Okta Without a Big Bang
An identity migration sounds terrifying because identity, if it breaks, breaks everything at once — so the discipline that makes it safe is to never flip the whole estate in one move. Stand up your chosen IdP and, at first, federate *to* Okta: your new provider becomes an OIDC/SAML broker that trusts the incumbent as an upstream source, so nothing changes for users yet. Then migrate applications one client at a time — re-point each app's OIDC discovery URL or SAML metadata from Okta to your provider, verify, and move on. Each app is an independent, reversible step, and the rollback is a one-line change back to the old issuer.
User accounts come across without a forced reset: federate during the bake period, provision into the new store with SCIM as apps cut over, and migrate passwords lazily by capturing them on each user's first login through the federated path. The migration is also the moment to fix what the hosted setup let you ignore — enforce PKCE everywhere, turn on passkeys, and wire SCIM deprovisioning so disabling an account actually removes access downstream, the control auditors ask about and hosted sprawl quietly erodes. Keep Okta authoritative until the owned plane has carried real traffic long enough to trust, exactly as a careful GitHub-to-self-hosted forge migration runs both systems in parallel before cutting the cord.
# An app's config is the whole migration surface: one issuer swaps for another.
# Before — trusting the hosted IdP:
oidc:
issuer: https://acme.okta.com
client_id: 0oa1a2b3c4DEF
scopes: [openid, profile, email]
redirect_uri: https://app.example.internal/callback
---
# After — trusting the IdP you operate. Same standard protocol, new home:
oidc:
issuer: https://idp.example.internal/realms/workforce
client_id: internal-dashboard
scopes: [openid, profile, email]
redirect_uri: https://app.example.internal/callback
pkce: S256 # enforce it now; the old setup let you skip it
# Rollback is the inverse edit. Because both speak OIDC, the app never knew
# it changed providers — which is the entire point of keeping the seam standard.The Exit Ramp From Your Own IdP
The failure mode of any self-hosting project is to escape one lock-in and quietly build another. The architecture here is designed against that: keep every seam a standard protocol, and treat anything proprietary as a convenience you must be able to live without.
- Apps consume standard OIDC and SAML. Their only dependency is a discovery document and a token format. Moving from Keycloak to Zitadel to Authentik is, from each app's perspective, an issuer URL change — the same move that brought it in.
- Users export over SCIM. The directory is not trapped in a proprietary schema; SCIM (RFC 7644) reads and writes accounts as standard JSON, so the user store walks to the next provider rather than being re-keyed by hand.
- Passkeys are WebAuthn credentials. Strong authentication rides the open FIDO2/WebAuthn standard, not a vendor SDK. The one honest caveat: a credential is bound to a relying-party identifier, so a domain change means re-enrolling factors — plan the identifier once, deliberately.
- The lock-in risk is the clever layer, not the core. Keycloak's fine-grained authorization, Authentik's elaborate flows, and Zitadel's actions are each useful and each the thing you cannot trivially export. Use them, but keep authorization decisions your apps can also derive from a standard token, so the IdP stays replaceable.
Held to that discipline, the token is the contract and the provider behind it is an implementation detail — optionality as a property you build rather than buy. Not the absence of an identity dependency, but one you can replace on your own schedule, through a migration that is per-app and reversible instead of a company-wide emergency.
Where Dex, Shibboleth and the Rest of the Field Fit
Three providers is a decision matrix, not a census. Two more come up constantly in the same evaluation, and they are usually raised for a reason that has nothing to do with Keycloak, Authentik, or Zitadel being wrong — they solve a narrower problem better, and knowing which problem you have saves you from operating an IdP heavier than your requirement.
Dex vs Keycloak: a connector, not a directory
Dex is an OIDC provider with pluggable connectors — a CNCF sandbox project, Apache-2.0, and a single small Go binary. The critical thing to understand is that it is not an identity directory. In practice it federates to something that is — LDAP or Active Directory, GitHub or GitLab, Google, an upstream SAML or OIDC provider — and re-issues the result as clean OIDC your applications can consume. It does ship a local password database (enablePasswordDB with staticPasswords, or accounts created over its gRPC API), but that exists for bootstrapping and lab use: there is no self-service registration, no MFA enrolment lifecycle, no groups or realms model behind it.
That makes the comparison less a contest than a category error. Keycloak is a full IAM system: it owns users, realms, groups, MFA enrolment, passkeys, self-service registration and password reset, fine-grained authorization, and identity brokering. Dex does one hop of protocol translation. If your requirement is *"let kubectl and a handful of cluster services authenticate against the corporate directory we already run"*, Dex is the right size and Keycloak is a database, a cluster, and a configuration model you did not need. If your requirement includes owning the directory itself, Dex cannot do it at any amount of configuration — it is not a missing feature, it is the design.
- Choose Dex when an upstream identity source already exists and is authoritative, the consumers are mostly Kubernetes and internal tooling, and you want the smallest thing that turns LDAP or GitHub into OIDC.
- Choose Keycloak when you need to *be* the directory: user lifecycle, MFA and passkey enrolment, SAML brokering for applications that speak nothing else, self-service flows, and authorization decisions that live with identity.
- Run both — a common and entirely sane pattern. Keycloak (or Authentik, or Zitadel) is the organizational IdP; Dex sits in a cluster as a lightweight local OIDC endpoint pointed at it, so cluster credentials stay short-lived and cluster-scoped.
Keycloak vs Shibboleth: SAML federation is a different discipline
Shibboleth comes from research and education, and it is still the default there for a reason that rarely surfaces in a commercial evaluation: multilateral federation. In InCommon, eduGAIN and their national equivalents, thousands of institutions and services trust each other through aggregated, signed metadata rather than through pairwise agreements, and they release attributes to each service under per-relying-party policy. Shibboleth's IdP was built around that model — metadata aggregation, attribute filter and resolver policy, and the SAML profile depth those federations assume.
Keycloak speaks SAML 2.0 competently and brokers it well, so for ordinary enterprise SAML — a dozen SaaS applications, one organization, bilateral trust — it is the easier system to run and the one with the broader protocol surface. The honest split is that Shibboleth is protocol-first where Keycloak is platform-first: Shibboleth's own product page still describes SAML and federation, with OIDC arriving as a separately installed plugin rather than a core protocol, while Keycloak treats OIDC as the primary path. If you must join an academic federation, Shibboleth is not a legacy choice you are tolerating — it is the tool that matches the trust model. If you are leaving Okta with a normal enterprise application estate, Shibboleth is a heavier, Java-based answer to a question you are not asking.
The wider shortlist, and when each earns a look
- [Ory](https://www.ory.sh/) (Kratos, Hydra, Keto) — headless and composable. You build the login UI and the flows; Ory supplies identity, OAuth 2.0/OIDC, and permissions as separate services. The right choice when identity is part of your product and you want it API-shaped rather than screen-shaped, and the wrong one when you wanted a working login page this week.
- [Authelia](https://www.authelia.com/) — a forward-auth companion to a reverse proxy rather than a full IdP. It gates applications behind SSO and 2FA with a small footprint and file or LDAP backends. It overlaps Authentik's proxy story at a fraction of the weight, and stops short of being an organizational identity provider.
- [FreeIPA](https://www.freeipa.org/) — host and Linux identity: Kerberos, LDAP, sudo policy, host enrolment, certificates. It answers *"who may log into these machines"*, which is a different question from *"who may log into these applications"*. Frequently deployed underneath Keycloak, not instead of it.
- Gluu, Casdoor and others — real projects with real deployments. Weigh them the way you would weigh the three above: governance and licence first, then whether the protocol surface matches the applications you actually have.
The Long Game: An Identity Plane You Can Audit in 2035
Identity is decade-horizon infrastructure. The accounts, roles, and trust relationships you define this year will still govern access long after today's tooling debate is forgotten, which is why each project's *governance* matters as much as its feature list. Keycloak under the CNCF, Authentik under a public-benefit company with an open core, Zitadel under a copyleft license and Swiss jurisdiction — different structures, and the right one is the one whose incentives you trust to still be aligned in 2035. Open source is the method, not a checkbox: the guarantee is not just that you can read the code, but that the project cannot be acquired out from under you and quietly re-priced.
None of this is a mandate to rip Okta out on Monday. Sovereignty is pursued in proportion to capacity: a team that cannot reliably run, patch, and fail over its own IdP should not bet every login on one yet — a poorly run identity plane is genuinely worse than a well-run hosted one. The credential and session data an IdP holds is among the most sensitive in the company, which makes the secrets and key management discipline around it non-negotiable. Staying on Okta with a tested, rehearsed exit plan is itself a sovereign decision; drifting deeper into a per-MAU dependency because leaving feels hard is not.
That breach is exactly the audit question every team whose logins all ran through one hosted vendor now has to be able to answer: which apps could have had sessions hijacked. For an organization with no visibility into a vendor's own support-system logs, that question has no boundary the organization set itself — the blast radius is whatever the vendor's incident turns out to be. The response worth building is not a panic migration off Okta — it is standing up an owned provider as a broker and moving apps one OIDC client at a time, deliberately, until identity is something that can be put in front of an auditor. The point was never to dislike Okta. It was to be able to leave it.
That is the sovereign identity plane in one idea: not a rejection of the hosted world, but the standing ability to step out of it on your own terms. Keycloak, Authentik, and Zitadel are three good providers with genuinely different trust models, but the architecture is the real product — one owned IdP brokering every login, joined to every app only by OIDC, SAML, and SCIM, replaceable without disturbing anything around it. Build it that way and the next change in someone else's pricing or terms becomes a memo you read, not an emergency you absorb.
§FAQ/Common questions
Frequently asked
What is the best self-hosted alternative to Okta in 2026?
There is no single best product, because the right choice depends on which constraint dominates your environment. Keycloak is the mature, CNCF-backed incumbent with the broadest protocol and identity-brokering support and an available vendor-supported build (Red Hat build of Keycloak) — the safe institutional default, at the cost of higher operational weight. Authentik is the strongest fit when you have many heterogeneous applications, including ones that speak no SSO, that all need to sit behind one login — its proxy outposts do forward authentication for apps that were never built for it. Zitadel suits multi-tenant SaaS and API-first teams that want identity managed as code and an event-sourced, tamper-evident audit trail. All three speak OIDC, OAuth 2.0, SAML 2.0, and passkeys, so the decision is a trust-model and operating-model question, not a protocol one.
Keycloak vs Authentik vs Zitadel — how do I choose?
Decide by the dominant constraint rather than the feature grid. Choose Keycloak for the widest protocol surface, deep SAML and identity brokering, a huge community, and the option of Red Hat commercial support — accepting that it needs an external PostgreSQL database, cluster coordination, and a real configuration learning curve. Choose Authentik when your problem is application-centric: a sprawl of internal and legacy tools that need a single authentication gate, solved with its forward-auth proxy outposts and customizable flow/stage model (and, since 2025.10, a Redis-free, PostgreSQL-only deployment). Choose Zitadel for multi-tenant B2B SaaS that wants first-class organizations, an API-first (gRPC/REST) management model, passkeys-first authentication, and an immutable event-sourced audit history — noting that since v3 (current line v4) the main repository is licensed AGPL v3.0.
Is a self-hosted identity provider secure enough for NIS2 or DORA audits?
It can be, and in some respects it is easier to evidence than a hosted IdP. NIS2 and DORA require documented access controls, rapid identity-based incident response, and audit trails you can produce on demand. Self-hosting puts the authentication logs, the user store, and the data-residency decision inside your own boundary, which makes those controls directly demonstrable — you are not asking a third party for log access you may not get. The trade is that you now own the operational security of the highest-value target in your estate: it must be patched, backed up, monitored, and failed over with discipline, and the secrets and signing keys it holds must be managed accordingly. A well-run self-hosted IdP is audit-grade; a neglected one is a liability, so the deciding factor is operational maturity, not the hosting model itself.
How do I migrate from Okta to a self-hosted IdP without downtime?
Migrate incrementally and keep Okta authoritative until the owned plane is proven. Stand up your chosen provider and federate it to Okta as an upstream identity source, so you have inserted a layer you control without changing anything for users. Then cut applications over one at a time by re-pointing each app's OIDC discovery URL or SAML metadata to your provider, verifying each before moving on — every app is an independent, reversible step. Provision users into the new store with SCIM as apps migrate, and migrate passwords lazily by capturing them on first login through the federated path so no mass reset is needed. Use the migration to enforce PKCE, enable passkeys, and wire SCIM deprovisioning. Keep Okta in place until the new IdP has carried real traffic long enough to trust; the rollback for any app is simply pointing its issuer URL back.
Dex vs Keycloak — which should I use?
They are not really competitors, and picking by feature list will mislead you. Dex is an OIDC provider with pluggable connectors that is not an identity directory: in practice it federates to an existing one — LDAP or Active Directory, GitHub, Google, an upstream SAML or OIDC provider — and re-issues the result as OIDC. It is a CNCF sandbox project, Apache-2.0, and a single small Go binary. It does ship a local password database (`enablePasswordDB` with `staticPasswords`, or accounts created over its gRPC API), but with no self-service registration, MFA enrolment lifecycle, or groups and realms behind it, that is a bootstrapping convenience rather than a directory. Keycloak is a full IAM system that owns the directory: users, realms, groups, MFA and passkey enrolment, self-service registration and password reset, SAML brokering, and fine-grained authorization. Choose Dex when an authoritative identity source already exists and you mainly need Kubernetes and internal tooling to authenticate against it — it is the smallest thing that turns LDAP or GitHub into OIDC. Choose Keycloak when you need to be the directory rather than point at one; no amount of Dex configuration gets you there, because it is the design rather than a missing feature. Running both is common and sensible: an organizational IdP for people, with Dex as a lightweight cluster-local OIDC endpoint in front of it.
Keycloak vs Shibboleth — is Shibboleth still the right choice?
It is, if you need multilateral federation; otherwise it is a heavier answer than your question. Shibboleth comes from research and education and remains the default there because of how those federations work: in InCommon, eduGAIN and their national equivalents, thousands of institutions and services trust each other through aggregated signed metadata rather than pairwise agreements, and release attributes per relying party under policy. Shibboleth's Identity Provider was built around exactly that — metadata aggregation, attribute resolver and filter policy, and deep SAML profile support. Keycloak speaks SAML 2.0 competently and brokers it well, so for ordinary enterprise SAML (one organization, bilateral trust, a set of SaaS applications) it is easier to operate and has the broader protocol surface. The split is protocol-first versus platform-first: Shibboleth's own product pages still centre on SAML and federation with OIDC available as a separately installed plugin, whereas Keycloak treats OIDC as the primary path. Joining an academic federation makes Shibboleth the correct tool, not a legacy compromise. Leaving Okta with a normal enterprise application estate does not.
What are the alternatives to Zitadel and Authentik?
The realistic self-hosted shortlist is Keycloak, Authentik, Zitadel, Dex, Ory, Authelia and Shibboleth, with Gluu, Casdoor and FreeIPA appearing in narrower contexts — but the field is wide and most of it is competent, so 'what else is like this' is the wrong sorting question. Three constraints actually decide it. First, who owns the user directory: Keycloak, Zitadel, Authentik and Ory own one, while Dex and Authelia federate to one that already exists. Second, which protocol your applications actually speak: SAML-heavy estates and academic federations pull toward Keycloak and Shibboleth, while a greenfield OIDC estate opens the whole field. Third, what the licence and governance will look like in five years, since identity is decade-horizon infrastructure — Zitadel's main repository moved to AGPL v3.0 at v3, Authentik runs an open core under a public-benefit company, and Keycloak sits under the CNCF. Ory is the pick when identity is part of your product and you want it API-shaped rather than screen-shaped; Authelia when you want SSO and 2FA in front of a reverse proxy without operating a full IdP.
How is self-hosted SSO different from SPIFFE/SPIRE workload identity?
They solve two different planes of the identity problem. SPIFFE/SPIRE provides machine (workload) identity: services prove themselves to each other with attested, short-lived cryptographic credentials (SVIDs) and mutual TLS, with no human and no password involved. Keycloak, Authentik, and Zitadel provide human identity: people authenticate interactively through OIDC, OAuth 2.0, or SAML, hold sessions, use MFA or passkeys, grant consent, and must be deprovisioned when they leave. The protocols, lifetimes, and failure modes are different, and neither tool replaces the other. A mature architecture runs both — cryptographic identity for workloads and a sovereign SSO provider for people — with a clear seam where a human's token authorizes an action a workload then carries out.
Further reading
- ITAR: export-controlled workloads and US-person access
- Schrems II, six years on: EU data on US clouds is still unsafe
- Digital Sovereignty: From Slogan to Testable Architecture
- Gaia-X in practice: federation, labels, and the sovereignty gap
- SPIFFE/SPIRE on Kubernetes: zero-trust workload identity and mTLS
- Kubernetes secrets management with External Secrets Operator and Vault
- NIS2, DORA, and the EU AI Act for self-hosted Kubernetes
- Forgejo, Woodpecker and Zot: CI Off GitHub Actions
- Capabilities: how Stribog builds sovereign platforms
- SOC 2 CC6 mapped to OIDC and RBAC on a self-hosted IdP
- OpenBao migration: three gates before you move a secret
- Pricing the exit: vendor lock-in as a number, not a feeling
- Secure by Design Is Now a Procurement Question
Executive Briefing
Thirty minutes to clarify your infrastructure risk
Walk us through your vendor footprint and regulatory constraints. We will tell you honestly where sovereignty creates leverage — and where it does not. No pitch deck. No obligation.