>
0Auth

Stop Paying Per User: Self-Host Your Auth with Keycloak

Stop paying per user: self-host your auth with Keycloak

I watched a friend’s bill for Auth0 go from $240 per month to $4,100
per month in eighteen months. The growth was organic. They were
building a B2B platform, and every new customer seat was another
padded line item on the invoice. The day the bill crossed $4,000,
they called me and asked if there was a real alternative. There
is. I helped them move to a self-hosted Keycloak deployment in
about three weekends, and the bill has been $0 per user per month
ever since. Here is the honest version of what that move looked
like, including the parts I would not put in a marketing post.

I am going to walk through what we replaced, how we migrated, what
we got, and what we gave up. The short version is that self-hosting
auth is one of the few infrastructure decisions that is a strict
win for almost any team past a certain size, and almost no team
under that size. The threshold is roughly the point at which your
per-user bill starts to dominate the conversation in your finance
review. For most teams, that is between 5,000 and 20,000 monthly
active users, depending on the per-user price.

What we were paying for

Auth0 is a good product. I want to say that up front because most
“switch from X” posts pretend the incumbent is broken. It is not
broken. It is just expensive at scale, and the pricing model
punishes growth in a way that makes the platform less attractive
the more successful it is. The 2024 Auth0 pricing page (the one we
were on) started at $35 per month for the B2B Essentials tier,
then added per-active-user fees above 1,000 users, then added
enterprise features (SAML, SCIM, advanced security) that pushed
you up to the next tier. By the time you had real B2B customers
and real SSO (single sign-on, a way for users to log in once and
get into multiple apps) requirements, you were at $0.10 to $0.25
per active user per month on the mid-tier plans, and well over
that on enterprise.

For my friend’s platform, with 32,000 active users and a long
list of enterprise customers who demanded SAML and SCIM (System
for Cross-domain Identity Management, a protocol for syncing user
accounts across systems), the bill was on track to hit $6,000 per
month within a year. They were profitable. They were not that
profitable.

Keycloak is the open-source identity and access management server
that Red Hat maintains and that the CNCF (Cloud Native Computing
Foundation, the home of Kubernetes and a lot of the open-source
cloud stack) has been shepherding. It is the same shape of product
as Auth0, Okta, Cognito, and the rest. It does OAuth 2.0, OIDC
(OpenID Connect, the identity layer on top of OAuth), SAML,
social login, passwordless, MFA (multi-factor authentication,
requiring a second verification step beyond a password), and most
of the enterprise features that get billed separately on the
commercial products. The community edition is free and ships under
the Apache 2.0 license.

The migration

We did not migrate in a single weekend. The first weekend was
spinning up the cluster. The second was writing the connectors.
The third was the cutover. The full timeline was about 21 calendar
days because we were being careful, and because we had to wait
two weeks for DNS (domain name system, the service that points
domain names to servers) propagation on one of the customer
identity providers.

Setting up the cluster itself is a small thing. We run three Keycloak
instances behind a Postgres database, all in a single AWS region,
fronted by an ALB (Application Load Balancer, AWS’s HTTP/HTTPS
load balancer). Total cost: about $90 per month for the EC2
instances, $40 for the database, and a few dollars for the load
balancer and DNS. That is the entire infrastructure bill. There
is no per-user line.

The connectors were the part I underestimated. Auth0 has a
slick UI for setting up SAML connections to enterprise identity
providers. Keycloak has the same feature, but the UI is the
admin console, not a wizard. Every SAML connection is a
configuration object with 30+ fields, half of which are spelled
differently in every IdP (identity provider, the system that
actually authenticates a user, e.g. Okta, Azure AD, Google
Workspace) on the other end. We had 14 enterprise customers with
custom IdPs, and configuring each one took a few hours of email
back-and-forth with their IT team. Total migration effort for the
connectors alone: about 40 hours of work, split across two
people.

What I had been dreading, the cutover, turned out to be the
easiest part. We ran Auth0 and Keycloak in parallel for two weeks.
New users went to Keycloak. Existing users authenticated against
both, with Keycloak as the source of truth. At the cutover, we
flipped the DNS. The platform did not notice. We had one user
report an issue, and it turned out to be a cached token (a
short-lived credential that lets a user stay logged in without
re-entering their password) on their laptop, which cleared in 15
minutes.

What we got

The headline number is the bill. We went from $4,100 per month
to $130 per month. Annualized, that is roughly $47,000 per year
in direct savings, before counting the projected growth that
would have pushed the Auth0 bill past $70,000 within a year.

  • Per-user pricing gone. The bill does not scale with users. This is the entire point. If you ship a feature that onboards 1,000 new users, the auth bill does not change.
  • Full enterprise feature set. SAML, OIDC, SCIM, social login, MFA, passwordless, custom themes. None of these are paywalled on the open-source build.
  • Customization without a feature request. Custom authentication flows, custom SPI (Service Provider Interface, Keycloak’s plugin extension point) implementations, custom event listeners, per-realm theming. All available, all free, all on the same code path.
  • Independence from vendor outages. Auth0 had two outages in 2024 that affected us. Keycloak had zero in the same period. The on-call rotation is calmer.

Beyond the bill, what we got is control. Auth0 had a feature we
needed: per-tenant custom branding on the login page. It was
available on the enterprise tier, which we were not on. With
Keycloak, we can theme the login page per realm (Keycloak’s
term for an isolated identity space, roughly equivalent to a
tenant), per client, or per flow. We can also add custom
authentication flows, custom SPI implementations, and custom
event listeners. None of that was possible on the Auth0 tier
we could afford.

Independence was a third, quieter win. As I noted above, Auth0
had two outages in 2024 that affected us. Keycloak had zero in
the same period. This is not a perfect comparison (we have a
smaller user base, and we have not yet had a Keycloak outage
at peak load), but the on-call rotation is calmer.

What we gave up

Self-hosting means we own the on-call. Keycloak did have two
issues in the first six months. One was a memory leak in the
session cache that we solved with a restart-and-monitor setup.
One was a bug in the SAML response parser that crashed a single
node. Both were fixable in an afternoon, but both happened on
weekends. That is the cost of self-hosting. You trade money for
time, and you trade the vendor’s on-call rotation for your own.

We also gave up the Auth0 marketplace. Auth0 has a directory of
third-party integrations (log streams, SIEM (security information
and event management, tools that aggregate security logs)
connectors, custom database action templates) that we used
lightly. Keycloak has most of the same functionality through
community plugins, but the documentation is patchier, and a few
of the plugins we depend on are maintained by a single developer.
For a small team that does not have the bandwidth to vet
maintainers, this is a real risk.

Finally, we gave up the easy upgrade path. Auth0 handles
upgrades for you. Keycloak does not. Major version upgrades
require a careful migration of the database schema, a coordinated
rolling restart, and a smoke test against every IdP connector.
We have a runbook for it, and we have done it twice. The first
time took about four hours. The second took about 90 minutes.
This is not a deal-breaker. It is a tax.

Trade-offs

Keycloak is not free in time. Our migration took three weekends
of focused work and about 40 hours of connector configuration.
Your math will be different if you have fewer enterprise
customers, fewer custom IdPs, or a smaller user base. Under
about 5,000 monthly active users, the per-user math on the
incumbent is probably cheaper than the engineering time.

The infrastructure bill is not zero. Our $130 per month is on
the low end; a more redundant multi-region deployment would be
closer to $500 per month. Either way, the bill is predictable
and does not scale with users, which is the entire point of
the migration.

Keycloak has a learning curve. The documentation is good but
dense. The admin console is functional but not pretty. The
configuration model is the right one for a complex identity
system, but it is not the right one for a team that just wants
to ship a sign-up form by Friday. If you are pre-product-market
fit, the commercial tools are probably the right choice. The
break-even is later.

If your team has the bandwidth to own the on-call and the
discipline to test upgrades before they ship, this is a clear
win at any user count over about 10,000. If your team is
shipping fast and breaking things, stay on the hosted tool until
the bill is the dominant concern.

Bottom line

If your auth bill is climbing with your user count, self-hosted
Keycloak will save you real money and give you real control. The
migration is real work, and the on-call is real work, and both
are worth it once the bill is the dominant concern. Do not do
this at 500 users. Do it at 10,000.

Leave a comment