| Tested on | Red Hat Enterprise Linux 10.2 (Coughlan) |
|---|---|
| Package | quay.io/keycloak/keycloak:26.7.3 |
| Applies to | Keycloak 26.x from ZIP or systemd, Docker, Podman, or Kubernetes — recovery uses the same bootstrap-admin command; stopping Keycloak and running it against your database differ by deployment |
| Privilege | Ability to stop every Keycloak node and run bootstrap-admin with the same database configuration as production |
| Scope | Recover lost admin access when master already exists — not first-time bootstrap (see Keycloak bootstrap admin) |
| Related guides | Create and manage Keycloak users Keycloak environment variables Install Keycloak with Podman |
You already have Keycloak installed, but you can no longer sign in to the Admin Console — wrong password, locked account, or interactive login (OTP/WebAuthn) blocking access.
This guide walks through admin recovery: create a temporary administrator with Keycloak’s offline bootstrap-admin command, reset your permanent admin, then remove the temporary account. It assumes the master realm already exists.
Quick answer: If another administrator can still sign in, open Users → select the affected admin → Credentials → Reset password. You do not need bootstrap-admin. Use bootstrap-admin only when no administrator can sign in.
Choose your Keycloak admin recovery path
| Situation | What to do |
|---|---|
| Forgot admin password (no other admin can sign in) | Normal recovery with bootstrap-admin user |
| Another administrator still works | Sign in as that admin → Users → Credentials → reset password — no bootstrap-admin needed |
| OTP or WebAuthn blocks login | Service-account recovery with bootstrap-admin service |
Changed KC_BOOTSTRAP_ADMIN_PASSWORD but old password still works |
Bootstrap env vars cannot reset an existing admin — use normal recovery |
| Recovery user works but production data missing | Wrong database — Recovery admin not working? Check the Keycloak database |
How Keycloak admin recovery works
bootstrap-admin is a Keycloak mechanism, not a Docker, Podman, Kubernetes, or ZIP feature. Your deployment only changes how you stop Keycloak and how you invoke kc.sh with the correct database settings.
The workflow in order:
- Stop every Keycloak node that uses this database before running
bootstrap-admin. - Run
bootstrap-adminagainst the same database the server uses. - Keycloak creates a temporary recovery administrator in
master. - Start Keycloak again.
- Sign in with the temporary account.
- Reset the permanent administrator (Admin Console or
kcadm.sh). - Delete the temporary user or service client.
The sections below translate these steps for each deployment type.
Step 1 — Stop every Keycloak node
Recovery modifies the Keycloak database. Every Keycloak process that connects to that database must be stopped first.
| Deployment | Example |
|---|---|
| systemd (ZIP) | sudo systemctl stop keycloak |
| Docker | docker stop keycloak |
| Podman | podman stop keycloak |
| Kubernetes (Deployment/StatefulSet) | kubectl scale deployment keycloak --replicas=0 -n keycloak |
| Kubernetes (Keycloak Operator) | Scale the Operator-managed Keycloak deployment to zero replicas, or follow the Operator recovery notes below |
External PostgreSQL does not need a separate “unlock” — only Keycloak nodes must stop. With embedded H2, a running server keeps the database file locked and bootstrap-admin fails with Database may be already in use.
Step 2 — Create a temporary recovery administrator
Use the same database configuration as production (kc.conf, data directory, or KC_DB_*).
Generic Keycloak (ZIP layout):
export RECOVERY_PASS='RecoveryAdm!2026'
bin/kc.sh bootstrap-admin user \
--username recovery-admin \
--password:env RECOVERY_PASS \
--no-promptIf the server normally runs start --optimized, add --optimized and keep runtime DB options only — see Recovery admin not working? Check the Keycloak database.
Expected output:
KC-SERVICES0077: Created temporary admin user with username recovery-adminIf you omit --username, Keycloak defaults to temp-admin.
ZIP or systemd
Run the command on the host with the same kc.conf / env as the server, while the service is stopped.
Docker or Podman
Run a one-off container with the same image tag and the same database configuration as the server. For start-dev with an embedded database, mount the same data volume. For PostgreSQL, pass the same KC_DB_* environment variables — not necessarily the same volume:
docker run --rm \
-v keycloak-data:/opt/keycloak/data \
-e RECOVERY_PASS='RecoveryAdm!2026' \
quay.io/keycloak/keycloak:26.7.3 bootstrap-admin user \
--username recovery-admin --password:env RECOVERY_PASS --no-promptReplace docker with podman where needed.
Kubernetes without Keycloak Operator
For a hand-written Deployment or StatefulSet, the recovery command must run from a Keycloak container that can reach the same production database as the running server. Scale Keycloak to zero replicas first.
Run a one-off Job or debug pod using:
- the same container image tag as production
- the same database Secret /
KC_DB_*env as the Keycloak Deployment - the same PVC only when your deployment stores embedded data on a PVC (uncommon in production; external PostgreSQL is typical)
The bootstrap-admin arguments match the generic example; only the pod wrapper differs. See Install Keycloak on Kubernetes for the Operator-managed StatefulSet layout.
Example Job skeleton:
apiVersion: batch/v1
kind: Job
metadata:
name: keycloak-recovery
namespace: keycloak
spec:
template:
spec:
restartPolicy: Never
containers:
- name: bootstrap-admin
image: quay.io/keycloak/keycloak:26.7.3
envFrom:
- secretRef:
name: keycloak-db
command:
- /opt/keycloak/bin/kc.sh
- bootstrap-admin
- user
- --username
- recovery-admin
- --password:env
- RECOVERY_PASS
- --no-prompt
env:
- name: RECOVERY_PASS
value: RecoveryAdm!2026Apply the Job only after every Keycloak pod is stopped.
Kubernetes with Keycloak Operator
Once master exists, the Operator ignores bootstrap environment variables on the Keycloak CR. Recovery requires running the bootstrap-admin CLI inside a pod that uses the same database configuration as the Operator-managed Keycloak instance.
Typical sequence:
- Scale the Operator-managed Keycloak deployment to zero replicas (or stop the Keycloak CR workload per your Operator version docs).
- Start a short-lived pod with the same Keycloak image and same database Secret / env as the CR.
- Run
kc.sh bootstrap-admin user …in that pod. - Scale Keycloak back up and continue with Step 3.
The official Operator documentation describes this CLI-based recovery path when bootstrap configuration no longer applies.
Step 3 — Start Keycloak and sign in
Start the server the way you normally do (systemctl start, docker start, kubectl scale … --replicas=1).
Sign in at /admin/ as recovery-admin. Optionally verify credentials with a token request — the command asks Keycloak for an access token using the recovery username and password:
curl -s -X POST 'http://localhost:8080/realms/master/protocol/openid-connect/token' \
-d 'client_id=admin-cli' -d 'grant_type=password' \
-d 'username=recovery-admin' -d 'password=RecoveryAdm!2026' \
| grep -o '"access_token"'An access_token line confirms the recovery account works.
Step 4 — Reset your permanent admin password
Creating recovery-admin only restores temporary access. Reset your permanent administrator next.
Admin Console (beginner-friendly)
- Sign in to
masteras the recovery user. - Open Users → select the
adminuser (or your permanent admin). - Open Credentials → Reset password.
- Enter the new password. For a permanent password, set Temporary to Off, then click Set password.
kcadm.sh (CLI alternative)
From the Keycloak install (or kubectl exec / docker exec into the running pod):
bin/kcadm.sh config credentials \
--server http://localhost:8080 --realm master \
--user recovery-admin --password 'RecoveryAdm!2026'Set a new password on the permanent admin user:
bin/kcadm.sh set-password -r master \
--username admin --new-password 'NewAdminPass!2026'Confirm the permanent admin can sign in before you delete the recovery account.
Step 5 — Delete the temporary recovery admin
In the Admin Console (signed in as the repaired permanent admin), open Users, delete recovery-admin or temp-admin.
Start any remaining cluster nodes and verify sign-in with the permanent admin. Do not leave a temporary recovery account in production.
Recover an admin locked by OTP or WebAuthn
If the administrator is locked out because interactive authentication requires OTP or WebAuthn — not because the password alone is wrong — a recovery user may not be enough.
A service account authenticates with a client ID and client secret instead of an interactive login. That lets kcadm.sh remove the OTP credential after you bring Keycloak back up.
Sequence:
- Stop every Keycloak node.
- Run
bootstrap-admin serviceto create a temporary service client. - Start Keycloak again.
- Authenticate
kcadm.shwith the service client. - Delete the OTP credential from the permanent admin user.
- Sign in as
adminwith password only, then delete the recovery client.
Stop every node, then create the service account:
export SERVICE_SECRET='SvcSecret!2026'
bin/kc.sh bootstrap-admin service \
--client-id recovery-sa \
--client-secret:env SERVICE_SECRET \
--no-promptStart Keycloak. Authenticate kcadm.sh:
bin/kcadm.sh config credentials \
--server http://localhost:8080 --realm master \
--client recovery-sa --secret 'SvcSecret!2026'Look up the admin user id:
bin/kcadm.sh get users -r master -q username=admin --fields id,usernameList credentials and find the OTP entry:
bin/kcadm.sh get users/USER_ID/credentials -r masterDelete the OTP credential (replace CREDENTIAL_ID with the id whose "type" is otp):
bin/kcadm.sh delete users/USER_ID/credentials/CREDENTIAL_ID -r masterSign in as admin with password only, then delete the recovery-sa client from Clients in master.
Use the same one-off container / Job pattern as Step 2 when Keycloak runs in Docker, Podman, or Kubernetes.
Recovery admin not working? Check the Keycloak database
If bootstrap-admin points at a different data directory or JDBC URL than production, the recovery account is created in the wrong database. Recovery appears to succeed but your real installation is unchanged.
| Server runtime | Recovery must match |
|---|---|
Embedded dev data (start-dev) |
Same /opt/keycloak/data volume or directory |
PostgreSQL start |
Same KC_DB, KC_DB_URL, username, and password |
| Optimized production | Same DB settings; add --optimized when the server uses start --optimized |
For PostgreSQL, pass the same KC_DB_* values to bootstrap-admin that you pass to start — see Configure Keycloak with PostgreSQL.
If the installation was built with kc.sh build and runs start --optimized, pass --optimized to bootstrap-admin as well. Otherwise Keycloak may run an implicit build before the command completes.
Keycloak admin recovery troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Database may be already in use / H2 file locked |
A Keycloak node still running (embedded H2) | Stop all Keycloak nodes before bootstrap-admin |
| Recovery login works but real data missing | Wrong database path or JDBC URL | Re-run against production volume or KC_DB_* |
Local access required after adding bootstrap env vars |
master already exists; vars cannot create admin |
Use bootstrap-admin user — see Step 2 |
| Changed bootstrap env; password unchanged | Bootstrap vars do not reset existing admins | Normal recovery workflow — not a bootstrap-env issue |
| Temporary banner after “fix” | Recovery user or client not deleted | Remove temporary user/client from master |
bootstrap-admin fails on password |
--no-prompt without --password:env |
Export password in env and reference it with --password:env VAR |
| Another admin can still sign in | Recovery not required | Reset affected user from Users → Credentials — no bootstrap-admin |
Summary
When you cannot sign in to the Keycloak Admin Console and no other administrator can help, stop every Keycloak node, run bootstrap-admin user against the same database as production, start the server, sign in as the temporary account, reset the permanent admin (Temporary → Off in the Admin Console for a permanent password), and delete the recovery user.
If another administrator can still sign in, reset the password from Users → Credentials instead. If OTP or WebAuthn blocks interactive login, use bootstrap-admin service and remove the OTP credential with kcadm.sh. For first-time installation, use Keycloak bootstrap admin instead — recovery is not the first-start path.
References
- Bootstrapping and recovering an admin account —
bootstrap-admin, temporary accounts, OTP recovery - Configuring Keycloak — configuration sources
- Keycloak Operator documentation — Operator-managed recovery when
masteralready exists - Server Administration Guide — user credential management
- Upgrading Guide —
KEYCLOAK_ADMINtoKC_BOOTSTRAP_ADMIN_*

