all posts

Top 7 Keycloak Hosting Platforms in 2026

Ajay Kumar··10 min read

Almost every Keycloak deployment starts the same way. Someone runs the container with start-dev, gets an admin console on port 8080 in about forty seconds, creates a realm, clicks together a client, and reports back that this was much easier than expected. Six weeks later that same person is reading a GitHub issue about redirect loops at two in the morning, discovering that the realm they hand-built lives only in a database nobody backed up, and learning that the H2 database in the image was never meant to hold production identities.

I'm Ajay, I build PandaStack, and we host Keycloak instances as ordinary apps — so I have an obvious interest in you self-hosting. That is exactly why I want to spend the first section arguing that a lot of teams should not. What follows is a shape comparison, not a price sheet. Vendor pricing and free-tier limits change on their schedule, so check current docs before you commit to anything.

First: should you self-host an identity provider at all?

Keycloak competes with Auth0, Okta, Clerk, WorkOS, Stytch and your cloud provider's own identity service. The pitch for Keycloak is usually cost and control: no per-monthly-active-user bill, no ceiling on how many enterprise SSO connections you sell, and every credential stays on infrastructure you own. Both of those are genuinely good reasons, and for teams selling into regulated markets or into the EU, data residency is often not negotiable.

The part people underestimate is what you have taken on. An identity provider is the single most availability-critical service you will run, because when it is down nobody can log into anything, including the tools you would use to fix it. You now own CVE response for a Java application on a schedule set by other people. You own upgrades — and Keycloak has had genuinely disruptive major versions, including the move from WildFly to Quarkus and repeated changes to hostname handling that broke working deployments. You own key rotation, backups of a database that contains everyone's identity, and a login page that has to stay up during your own deploys.

A useful test: if your team cannot currently articulate who is paged when Keycloak stops serving tokens at 3am, and what the recovery procedure is, you are not ready to self-host an identity provider. That is not a judgement — it is a genuinely different operational commitment from hosting a web app.

Pick a hosted IdP when identity is not your product, your headcount is small, and per-user pricing at your scale is cheaper than a fraction of an engineer. Pick Keycloak when the per-user or per-connection bill has become a real line item, when data residency is a contractual requirement, or when you need protocol-level control that a hosted product does not expose. Both answers are defensible. The wrong answer is picking Keycloak on cost grounds and then discovering the ops cost, which is the failure mode I see most.

What Keycloak actually needs from a host

Keycloak is a Java application built on Quarkus. That gives you three requirements that quietly rule out several hosting options before you have compared anything.

  • A JVM with real memory. Keycloak is not a 128MB serverless function. Plan on a gigabyte as a floor for a small instance and more for anything with real traffic, and remember the JVM will happily grow into whatever heap you give it. Sizing it like a Node app is how you get OOM kills during a login storm.
  • An external relational database. The embedded dev-mode database exists so the container starts with no dependencies. It is not for production, it does not survive a container replacement, and it does not let you run more than one instance. Postgres is the common choice; MariaDB, MySQL, Oracle and SQL Server are also supported.
  • Correct knowledge of its own public URL. Keycloak issues tokens and redirects that embed the hostname it believes it is serving on. Behind a reverse proxy or load balancer, if that belief is wrong, logins fail in ways that look like anything except a config problem.

start-dev and start are two different products

This is the single most common production mistake. start-dev turns on the dev profile: HTTP is enabled without TLS, hostname strictness is relaxed, caching is local-only rather than distributed, and the embedded database is used unless you point elsewhere. It is designed to make the first forty seconds pleasant. start is the production mode — it expects a real database, expects HTTPS to be terminated somewhere, and refuses to guess at settings you have not supplied.

Running start-dev in production is not a performance footnote. It is a security posture difference, and it means your instance is one restart away from losing state if you left the database on defaults.

#!/usr/bin/env bash
# Production start. Note: 'start', not 'start-dev'.
set -euo pipefail

# --- External database. The dev-mode embedded DB is not an option here. ---
export KC_DB=postgres
export KC_DB_URL="jdbc:postgresql://${DB_HOST}:5432/keycloak?sslmode=require"
export KC_DB_USERNAME="${DB_USER}"
export KC_DB_PASSWORD="${DB_PASSWORD}"
export KC_DB_POOL_MAX_SIZE=20

# --- Identity of the deployment. Get this wrong and logins redirect-loop. ---
export KC_HOSTNAME="https://id.example.com"
export KC_HTTP_ENABLED=true          # TLS is terminated at the proxy in front
export KC_PROXY_HEADERS=xforwarded   # trust X-Forwarded-Proto / -Host / -For
export KC_HTTP_PORT="${PORT:-8080}"

# --- Health + metrics so your platform can tell if it is actually alive. ---
export KC_HEALTH_ENABLED=true
export KC_METRICS_ENABLED=true

# Bootstrap admin is for first boot only; delete these vars once a real
# admin user exists in the master realm.
export KC_BOOTSTRAP_ADMIN_USERNAME=admin
export KC_BOOTSTRAP_ADMIN_PASSWORD="${KC_ADMIN_PASSWORD}"

# --optimized skips the build step at boot; it requires that you already ran
# 'kc.sh build' with the same feature/db flags during your build phase.
exec /opt/keycloak/bin/kc.sh start --optimized

The hostname and proxy settings that break every first deploy

The symptom is always the same and it is always misdiagnosed. You click login, the browser bounces between your app and Keycloak until the browser gives up, or the admin console loads with no CSS, or a token comes back with an issuer your resource server rejects. People go looking for a client misconfiguration. It is almost never the client.

What is happening is that your proxy terminates TLS and forwards plain HTTP to Keycloak, so Keycloak sees an http request on some internal hostname and writes that into its issuer claim, its redirect URLs and its asset paths. The browser is on https and on your public domain. The two do not match. Setting KC_HOSTNAME to the full public URL and KC_PROXY_HEADERS to xforwarded fixes it — but only if your proxy actually sets those headers, and only if you are not also passing them through from an untrusted hop. Note that hostname handling changed materially in Keycloak 26; older blog posts telling you to set KC_HOSTNAME_STRICT and a bare hostname are describing a different version.

Only enable KC_PROXY_HEADERS when a proxy you control is the sole path to Keycloak. If clients can reach the instance directly, they can forge X-Forwarded-Host and make Keycloak generate password-reset links pointing at an attacker's domain. Trusting forwarded headers is safe behind a proxy and a real vulnerability without one.

Export your realm, or you do not have a deployment

A realm built by clicking exists in exactly one place: rows in a database. You cannot review it, diff it, or recreate it in staging. Export it to JSON, keep it in git, and import it at boot. The export is also how you migrate hosts, how you rebuild after a bad upgrade, and how you give a new engineer a realm to develop against that matches production.

# --- Export a realm to JSON you can commit ---
# Runs against the same database the server uses; stop the server first, or
# use a copy of the DB, since export locks things it touches.
/opt/keycloak/bin/kc.sh export \
  --dir /tmp/realm-export \
  --realm myrealm \
  --users realm_file

# Redact before committing. The export can contain client secrets and,
# with --users, password hashes. Keep secrets in your secret store.
ls /tmp/realm-export
# myrealm-realm.json  myrealm-users-0.json

# --- Import it on a fresh instance, at boot ---
/opt/keycloak/bin/kc.sh start --optimized \
  --import-realm            # reads every *.json under /opt/keycloak/data/import

# One-shot import into an existing install (overwrites the realm):
/opt/keycloak/bin/kc.sh import \
  --file /opt/keycloak/data/import/myrealm-realm.json \
  --override true

The honest caveat: realm JSON is a full-state document, not a migration format. Round-tripping it across major Keycloak versions can surprise you, and merging two people's edits is unpleasant. Treat it as a reproducible baseline plus disciplined change review rather than as Terraform. If you want true declarative management, the keycloak-config-cli project and the Terraform provider both exist and both have real users.

The seven hosting options

1. Red Hat build of Keycloak

The commercially supported distribution, successor to Red Hat Single Sign-On. You get a supported version with a defined lifecycle, security backports, and a vendor whose support contract you can point at during an audit. It is the answer when Keycloak is load-bearing for a regulated business and 'the community upstream' is not an acceptable answer to a compliance questionnaire. It runs on OpenShift or plain hosts, and the trade is the usual enterprise one: you pay for support and lifecycle, and you run a version that lags upstream by design.

2. Managed Keycloak vendors (Cloud-IAM, Phase Two and similar)

A small but real category: vendors who run upstream Keycloak for you, with the database, backups, TLS, upgrades and monitoring handled, while you keep full admin-console access and standard realm exports. This is a genuinely underrated middle ground. You keep protocol control and portability — your realm JSON runs anywhere Keycloak runs — without owning the 3am page. Several offer EU-hosted or single-tenant deployments, which is often the actual reason people wanted Keycloak in the first place.

Check three things before signing: which Keycloak versions they support and how fast they follow upstream, whether you get database access or only the admin API, and what the exit path looks like. If you cannot export a realm and stand it up elsewhere, you have lost the portability that justified choosing Keycloak.

3. Kubernetes with the Keycloak Operator

The official operator manages a Keycloak custom resource plus KeycloakRealmImport, so a realm can be applied like any other manifest, and it handles rolling upgrades and the Infinispan clustering config that multi-instance Keycloak needs. If you already run Kubernetes with a working GitOps pipeline and a database operator, this is the most complete story on the list.

The usual caveat applies with force: this is right when the cluster already exists and wrong when you would be adopting Kubernetes to host one identity provider. Clustered Keycloak also introduces distributed cache behaviour — sessions live in Infinispan, and a rolling upgrade across incompatible cache versions can log everyone out. Read the operator's upgrade notes before you assume rolling deploys are free.

4. A plain VM you manage yourself

One EC2 or Hetzner box, a JVM, a systemd unit, nginx or Caddy in front, and a Postgres somewhere. This is unfashionable and it is completely fine for a large number of deployments. It is cheap, it is transparent, and there is no abstraction between you and the process when something breaks. It is also the option where every single ops responsibility is yours: patching the OS, renewing certificates, monitoring the JVM, backing up the database, and having a plan for the box dying.

Pick it if you already run VMs competently and want the lowest possible bill and the fewest moving parts. Avoid it if 'we will set up backups later' is a sentence anyone on the team has said this quarter.

5. Docker on a container PaaS (Render, Railway, Fly and friends)

Point a PaaS at the official Keycloak image, attach the platform's managed Postgres, set the environment variables, done. This is the fastest path from nothing to a working HTTPS Keycloak, and for a staging or internal instance it is hard to beat.

Two things to verify on whichever platform you choose. First, the memory tier: a JVM on a 512MB instance will thrash or die, and container platforms often default low. Second, how the platform's proxy sets forwarded headers and whether health checks hit the right path — Keycloak's health endpoints live under /health when KC_HEALTH_ENABLED is on, and a platform probing / on a fresh instance may decide the app is unhealthy and restart it in a loop. Persistent disk matters much less here than it does elsewhere, because your state is in Postgres, which is exactly how it should be.

6. PandaStack

This is my product, so read it as an interested party's description. A PandaStack app is a full Ubuntu 24.04 userspace inside a Firecracker microVM, deployed from a git repo, with hardware KVM isolation rather than a shared container runtime. For Keycloak that maps unusually well: a JVM wants a real Linux userland, a real process tree and real memory, and that is what a microVM is. You install the Keycloak distribution in the build step, run kc.sh start in the start step, and the app gets a stable HTTPS URL you can point KC_HOSTNAME at.

The second half is the database. Keycloak needs an external RDBMS, and our managed Postgres 16 is exactly that: one dedicated microVM per database with a durable volume, a TLS connection string, point-in-time restore, clone-to-a-new-database for testing an upgrade against real data, failover and credential rotation. Creation takes 30 to 90 seconds. Attaching it is environment variables, which is the same shape the KC_DB_URL config already wants.

Now the constraints, because they are real. Guest RAM on an app is fixed by the template snapshot at restore time, so you choose a memory tier rather than resizing a running instance — size it for a JVM up front, not for a Node process. And more importantly: turn scale-to-zero off for this workload. Our apps hibernate when idle and wake in about 1.2 seconds, which is a great trade for an internal dashboard and a bad trade for an identity provider that every other service authenticates against. A cold wake sitting in the login path is user-visible, and a token endpoint that occasionally takes a second longer than usual is the kind of thing that makes downstream service timeouts fire. Keep the IdP always-on and spend the scale-to-zero savings on the apps behind it.

Where we are the wrong answer: we do not offer managed Keycloak. We host the app and the database it needs; the upgrades, the realm config and the CVE response are still yours. If you wanted someone else to own those, one of the managed Keycloak vendors or a hosted IdP is the honest recommendation.

7. Don't host Keycloak: Auth0, Okta, Clerk, WorkOS

The option that belongs in every Keycloak comparison and rarely appears in one. Auth0 and Okta are the enterprise defaults with deep protocol coverage and compliance paperwork already done. Clerk is aimed at product teams who want drop-in UI components and a good developer experience. WorkOS is aimed specifically at the 'we need to sell enterprise SSO and SCIM next quarter' problem. Each of them removes the entire operational category we have been discussing.

The reasons to take Keycloak anyway are still good: cost at scale, data residency, single-tenant deployment, protocol-level control, and no vendor able to reprice the thing your login page depends on. Just make that choice with the ops cost on the table rather than discovering it later.

Side by side

  • Red Hat build of Keycloak — Supported distribution with a defined lifecycle and security backports. Ops burden: you still operate it, with a vendor behind you. Best for: regulated businesses that need a support contract to point at.
  • Managed Keycloak vendors — Upstream Keycloak run for you, admin console and realm exports intact. Ops burden: low, and portability preserved. Best for: teams who chose Keycloak for residency or cost but do not want the pager.
  • Kubernetes plus the Keycloak Operator — Declarative Keycloak and realm imports, clustering handled. Ops burden: inherits your cluster's. Best for: shops already running Kubernetes with GitOps.
  • A plain VM — JVM, systemd, a proxy, and a Postgres. Ops burden: entirely yours. Best for: teams who already run VMs well and want the smallest bill and the fewest layers.
  • Docker on a container PaaS — Official image plus the platform's managed Postgres. Ops burden: low, with memory tiers and health-check paths to verify. Best for: staging and internal instances you want running today.
  • PandaStack — Keycloak as a git-deployed app in a Firecracker microVM with managed Postgres 16 attached by env var. Ops burden: yours for upgrades and realm config, ours for the machine and the database. Best for: teams who want a real Linux VM for the JVM plus a managed database from one place — with scale-to-zero deliberately switched off.
  • A hosted IdP (Auth0, Okta, Clerk, WorkOS) — No Keycloak at all. Ops burden: none. Best for: teams where identity is not the product and per-user pricing is cheaper than the engineering time.
Nobody regrets self-hosting their identity provider on the day they deploy it. They regret it on the day of the CVE.

A pre-production checklist

  1. You are running start, not start-dev, and you have confirmed it by checking the startup log rather than by assuming.
  2. KC_DB points at an external Postgres with TLS, and that database has automated backups you have restored from at least once.
  3. KC_HOSTNAME is the full public URL and KC_PROXY_HEADERS is set only because a proxy you control is the only route in.
  4. The realm is exported to JSON, committed, and imported on boot — with client secrets held in your secret store, not in the committed file.
  5. The instance is always-on, has a health check pointed at Keycloak's own health endpoint, and someone specific is paged when it fails.
  6. You have written down your upgrade procedure, including how you will test the next major version against a clone of production data before it touches users.

The short version

If identity is not your product and the per-user bill is small, use a hosted IdP and skip this entire article. If you have real reasons to run Keycloak but not to operate it, a managed Keycloak vendor keeps your portability and drops the pager. If you need a support contract, buy the Red Hat build. If you already run Kubernetes, use the operator. If you want the simplest possible self-hosted setup, a plain VM or a container PaaS with managed Postgres will serve you for years. And if you want a real Linux microVM sized for a JVM with a managed Postgres alongside it, that is the shape we are good at — with the identity provider pinned always-on.

Whichever you pick, the same four things decide whether you are happy in a year: production mode, an external database with tested backups, a hostname configuration that matches what the browser sees, and a realm that lives in git rather than in someone's browser history.

Frequently asked questions

Can Keycloak run without an external database?

Only in development. The container image ships an embedded database so start-dev works with no dependencies, but it is explicitly not supported for production: it disappears when the container is replaced, it cannot be backed up in any sane way, and it prevents running more than one instance. For anything real, set KC_DB and KC_DB_URL to point at an external RDBMS. Postgres is the most common choice; MariaDB, MySQL, Oracle and SQL Server are also supported. Configure this on day one, because migrating identities out of the embedded database later is painful.

Why does Keycloak redirect-loop behind my reverse proxy?

Almost always because Keycloak does not know its own public URL. Your proxy terminates TLS and forwards plain HTTP, so Keycloak sees an http request on an internal hostname and writes that into its issuer claim, redirect URLs and asset paths, while the browser is on https and your public domain. Set KC_HOSTNAME to the full public URL, for example https://id.example.com, and set KC_PROXY_HEADERS to xforwarded so Keycloak trusts X-Forwarded-Proto and X-Forwarded-Host. Only enable that header trust when a proxy you control is the only route to the instance.

How much memory does Keycloak need?

More than people budget for, because it is a JVM application rather than a small Go or Node process. A gigabyte is a reasonable floor for a small instance, and busier deployments want more, particularly if you cache many sessions. Container platforms that default to 256MB or 512MB tiers will produce OOM kills that look like random restarts during login storms. Size the instance for the JVM heap plus overhead, set explicit heap limits rather than letting it grow into whatever it finds, and load-test the token endpoint before you decide the tier is fine.

Should I export my Keycloak realm as code?

Yes. A realm built by clicking exists only as rows in a database — you cannot review it, diff it, or recreate it in staging. Use kc.sh export to write realm JSON, commit it, and start with --import-realm so a fresh instance comes up configured. Redact client secrets before committing and keep them in your secret store. Be aware that realm JSON is full-state rather than a migration format, so round-tripping across major versions can surprise you; if you want stricter declarative management, keycloak-config-cli and the Terraform provider both exist.

Can I run Keycloak on PandaStack?

Yes. A PandaStack app is a full Ubuntu 24.04 userspace in a Firecracker microVM, so the JVM runs normally, and our managed Postgres 16 is the external database Keycloak requires — attached by environment variable, created in 30 to 90 seconds, with point-in-time restore and clone-to-a-new-database for testing upgrades. The app gets a stable HTTPS URL to set KC_HOSTNAME to. Two honest constraints: guest RAM comes from the template tier so size it for a JVM up front, and turn scale-to-zero off, because a cold wake in the login path is user-visible.

Is Keycloak actually cheaper than Auth0 or Okta?

The licence is free and the hosting is cheap, so at high user counts the arithmetic often favours Keycloak by a wide margin. What the comparison usually omits is engineering time: CVE response for a Java application, major-version upgrades that have historically been disruptive, database backups you have actually tested restoring, and being on call for the one service whose outage prevents anyone logging in to fix it. Price a realistic fraction of an engineer into the comparison. If that still wins, self-hosting is the right call.

Keep reading

Run code in a microVM in one API call.

49ms p50 cold start. Fork, snapshot, and scale to zero.

Start free
Written by Ajay Kumar, Founder, PandaStack.