Skip to main content

Run Keycloak Locally in 5 Minutes

One command gets you a running Keycloak with an admin account:

docker run -p 127.0.0.1:8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.7.3 start-dev

Open http://localhost:8080 and sign in with admin / admin. That is the whole thing.

The rest of this page is the part that matters later: what start-dev actually did, why you must not build on it, and where your data went.

Tested against

Keycloak 26.7.3. The admin bootstrap variables changed in Keycloak 26 — if you find a guide using KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD, it predates that change.

Prerequisites

Docker, Podman, or a JDK 21+. Nothing else.

Start it

Docker (recommended)
docker run -p 127.0.0.1:8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.7.3 start-dev

Add -d --name keycloak to run it in the background.

Podman

Identical, because the image is the same:

podman run -p 127.0.0.1:8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
quay.io/keycloak/keycloak:26.7.3 start-dev
Zip distribution (no container runtime)
curl -LO https://github.com/keycloak/keycloak/releases/download/26.7.3/keycloak-26.7.3.zip
unzip keycloak-26.7.3.zip && cd keycloak-26.7.3
export KC_BOOTSTRAP_ADMIN_USERNAME=admin
export KC_BOOTSTRAP_ADMIN_PASSWORD=admin
bin/kc.sh start-dev

Needs JDK 21 or later on your PATH.

Verify it worked

Don't trust the browser — ask the server:

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/realms/master

200 means Keycloak is up and the master realm is serving. Anything else and it is still starting or something below went wrong.

For the full picture of what this instance can do:

curl -s http://localhost:8080/realms/master/.well-known/openid-configuration | jq .

Startup takes a few seconds. On a recent laptop:

Keycloak 26.7.3 on JVM (powered by Quarkus 3.33.3.1) started in 4.659s.
Listening on: http://0.0.0.0:8080

Roughly 10 seconds from docker run to a 200 on the realm endpoint, most of it JVM start and the first-run database bootstrap.

What start-dev actually did

This is the part the quickstarts skip, and it is the reason people get stuck two weeks later.

Look at your logs and you will find this on the first line:

Running the server in development mode. DO NOT use this configuration in production.

start-dev is not "Keycloak with logging turned up." It is a materially different configuration:

start-devstart (production)
DatabaseIn-container H2Postgres/MySQL/etc, required
HTTPSDisabledRequired unless explicitly relaxed
HostnameInferred from the requestMust be set explicitly
CachingLocal onlyDistributed (Infinispan)
Theme/config cachingOff, so edits appear instantlyOn
StartupRe-derives config each timeWants an optimized build

Two practical consequences:

  1. Things that work in dev will fail in production — most often hostname and proxy settings, because dev mode guesses and production refuses to. That is not Keycloak being awkward; guessing your public hostname is how you get token issuer mismatches and open redirects.
  2. Config that is fine in dev is not a starting point for prod. You do not "turn off dev mode." You configure a real database, TLS, and hostname, and run start.

For now, dev mode is exactly right. Just don't build a deployment on it.

Where your data lives

start-dev uses an H2 file database inside the container. That leads to a result people find surprising:

docker restart keycloak # data survives
docker rm -f keycloak # data is gone, permanently

Verified: after docker restart, a realm created earlier still returns 200. After docker rm -f and starting a fresh container, the same realm returns 404.

If you want your experiments to survive removing the container, mount the data directory:

docker run -p 127.0.0.1:8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
-v keycloak_data:/opt/keycloak/data \
quay.io/keycloak/keycloak:26.7.3 start-dev

H2 is for local development only. It is not a supported production database and it will not survive a second Keycloak node.

Troubleshooting

Bind for 127.0.0.1:8080 failed: port is already allocated Something already holds 8080 — very often a previous Keycloak container. docker ps to find it, docker rm -f <name> to clear it, or publish a different port with -p 8081:8080.

You can't reach Keycloak from another machine or VM. The 127.0.0.1: prefix in -p 127.0.0.1:8080:8080 binds the port to loopback only, which is the right default. To expose it on your network, drop the prefix: -p 8080:8080. Do this only on a trusted network — this instance has a password of admin and no TLS.

The admin user isn't created on a later start. Bootstrap only runs against an empty database. You will see KC-SERVICES0077: Created temporary admin user with username admin exactly once, on first start; subsequent starts skip it and changing the environment variables does nothing. To get a new admin on an existing database, use kc.sh bootstrap-admin.

Note the wording: temporary admin user. It is intended to get you in so you can create a real one, not to be a permanent account.

Startup hangs or the container exits immediately. Check docker logs <name>. On the zip distribution the usual cause is a JDK older than 21.

Next steps