Upgrading Keycloak: A Practical Migration Guide from Legacy to Modern Versions
Last updated: March 2026
Introduction: Why Upgrading Keycloak Is Non-Negotiable
If you are running Keycloak 8, 12, or any version older than 21, you are operating on software that no longer receives security patches. Every month you delay an upgrade widens the gap between your deployment and the current release, making the eventual migration harder and your authentication layer more vulnerable.
Keycloak has evolved dramatically since its early WildFly days. Between versions 8 and 26, the project replaced its entire runtime (WildFly to Quarkus), redesigned its admin console, restructured its configuration system, introduced persistent user sessions, and added native multi-tenancy through Organizations. These are not cosmetic changes. They represent a fundamentally different product.
The critical thing to understand before you start: you cannot jump directly from Keycloak 8 to 24 or 26. The database schema migrations, configuration format changes, and SPI contract shifts are too significant. Attempting a direct leap will result in failed startups, corrupted data, or silently broken authentication flows. You need to move through intermediate versions, testing at each stop.
This guide walks you through the entire process, from understanding what changed at each major milestone to executing a safe, repeatable migration strategy.
Understanding Keycloak’s Major Version Milestones
Before planning your migration path, you need to understand what changed and when. Each version range introduced architectural shifts that affect how you configure, deploy, and extend Keycloak.
Keycloak 8-16: The WildFly Era (Java EE)
These versions ran on WildFly (formerly JBoss AS) and used standalone.xml or standalone-ha.xml for all configuration. Clustering relied on JGroups and Infinispan embedded within the WildFly container. Custom themes used FreeMarker templates, and SPIs followed the Java EE service loader pattern.
If you are starting from this era, your configuration lives in XML files, your deployment scripts reference WildFly CLI commands, and your custom extensions compile against WildFly-specific APIs.
Keycloak 17: The Quarkus Preview
Version 17 was the inflection point. It shipped both the legacy WildFly distribution and a new Quarkus-based distribution. The Quarkus distribution used a flat configuration file (conf/keycloak.conf) or environment variables instead of XML. Startup times dropped from 30+ seconds to under 5 seconds. The internal architecture shifted from Java EE to a native Quarkus build.
This version is important as a migration waypoint because it lets you run the new runtime against your existing database while still having the WildFly distribution as a fallback.
Keycloak 18-19: Quarkus Becomes Default
WildFly was officially deprecated. The Quarkus distribution became the recommended (and eventually only) option. Docker images switched to the Quarkus base. If your CI/CD pipelines referenced the old image tags or entrypoints, they broke here.
Keycloak 20: WildFly Distribution Removed
Starting with version 20, the WildFly distribution was no longer published. If your deployment automation depended on standalone.xml, WildFly CLI scripts, or the old Docker image format, you had to rewrite everything. The Spring Security adapters (keycloak-spring-boot-starter, keycloak-spring-security-adapter) were also deprecated and later removed.
Keycloak 21: New Admin Console (v3)
The legacy admin console was removed entirely. The new React-based admin console (v3) became the only option. If you had trained your operations team on the old console or built internal documentation with old screenshots, everything needed updating. Authentication flow configuration also changed in how it was presented and managed.
Keycloak 22: Hostname v2 Configuration
The hostname configuration was overhauled. The old --hostname-frontend-url, --hostname-admin-url pattern was replaced with a simplified v2 scheme using --hostname and --hostname-admin. Reverse proxy configurations that relied on v1 hostname settings needed adjustment.
Keycloak 24: Organizations and Persistent Sessions
Keycloak 24 introduced the Organizations feature for native multi-tenancy, allowing you to group users and configure identity providers per organization without separate realms. Persistent user sessions were also added, meaning sessions survive server restarts without relying solely on Infinispan distributed caches.
Keycloak 26: The Current Release
As of early 2026, Keycloak 26 represents the latest stable release with continued refinements to Organizations, improved metrics and health endpoints, and ongoing performance optimizations for the Quarkus runtime.
Recommended Migration Path
The safest approach is to migrate through specific intermediate versions, testing thoroughly at each stop. Here is the recommended path:
Hop 1: From 8.x to 12.x
Keycloak 12 is the last version before significant internal restructuring began. Moving from 8 to 12 keeps you within the WildFly ecosystem while picking up several years of database schema changes and bug fixes. The configuration format remains standalone.xml, so your existing tooling continues to work.
Key changes to verify:
- Database schema migrations (run automatically on startup)
- Authentication flow behavior (check any custom flows)
- Token format changes (verify JWT consumers still work)
Hop 2: From 12.x to 17.x
Version 17 is your bridge between WildFly and Quarkus. Start by running 17 with the WildFly distribution against your migrated database to ensure schema compatibility. Then test the Quarkus distribution to prepare for the next hop.
Key changes to verify:
- All WildFly-era functionality still works
- Begin translating
standalone.xmlsettings tokeycloak.confformat - Test custom themes and SPIs against both distributions
Hop 3: From 17.x to 21.x
This is the most significant hop. You are moving fully to Quarkus, adopting the new admin console, and leaving WildFly behind permanently. Your standalone.xml must be fully translated to keycloak.conf or environment variables.
Key changes to verify:
- Configuration is fully migrated to Quarkus format
- Custom themes render correctly in new console
- SPIs compile and load against Quarkus runtime
- Authentication flows behave identically
- All API integrations work with the new admin REST API responses
Hop 4: From 21.x to 24.x or 26.x
The final hop brings you to modern Keycloak. By this point your configuration, themes, and SPIs are already Quarkus-native. The main concerns are hostname v2 configuration changes and verifying compatibility with newer features.
Key changes to verify:
- Hostname configuration migrated to v2 scheme
- Persistent user sessions work correctly
- Any deprecated features you rely on have been removed
- Performance and startup behavior match expectations
Why You Must Not Skip Hops
Each intermediate version runs its own set of database migration scripts (Liquibase changesets). These scripts are designed to run sequentially. If you skip from version 8 directly to 24, the migration engine may encounter changesets that assume prior migrations have already run, leading to failures or data corruption. The application-level changes (configuration, SPIs, themes) are equally cumulative.
Breaking Changes to Watch For
WildFly to Quarkus: Configuration Overhaul
This is the single largest breaking change in Keycloak’s history. Every setting that lived in standalone.xml must be translated to either keycloak.conf key-value pairs or environment variables.
WildFly (standalone.xml) |
Quarkus (keycloak.conf) |
|---|---|
<datasource> XML block |
db=postgres, db-url=jdbc:postgresql://... |
| JGroups subsystem config | cache-config-file or managed externally |
| Logging subsystem | log-level=info, log-console-output=json |
| HTTPS/SSL keystore config | https-certificate-file, https-certificate-key-file |
| Proxy settings | proxy-headers=xforwarded |
There is no automated translation tool. You must manually map each setting.
Database Schema Migrations
Keycloak uses Liquibase to manage schema changes. On startup, it detects the current schema version and applies pending changesets. This process is generally reliable, but:
- Always back up before starting a new version. A failed migration can leave your schema in a partially upgraded state.
- Migration can take significant time on large databases (millions of sessions, thousands of users). Plan for downtime.
- Some migrations are destructive. Columns get dropped, tables get restructured. There is no automated rollback.
Theme Changes
FreeMarker templates have been restructured multiple times across major versions. If you have custom themes or branding, expect to:
- Update template file names and directory structures
- Adjust FreeMarker variable references (some variables were renamed or removed)
- Rebuild CSS/JS assets for the new admin console
- Test every user-facing page (login, registration, password reset, OTP, error pages)
SPI and Extension Changes
Custom providers (authenticators, event listeners, user storage providers, protocol mappers) may need recompilation against new Keycloak libraries. The SPI interfaces have changed method signatures in several versions. If you use custom extensions, plan for development time to update and test each one.
Hostname Configuration (v1 to v2)
If you upgraded to a version between 17 and 21 using hostname v1, you will need to migrate again when moving to 22+:
# v1 (deprecated)
hostname-frontend-url=https://auth.example.com
hostname-admin-url=https://admin.auth.example.com
# v2 (current)
hostname=https://auth.example.com
hostname-admin=https://admin.auth.example.com
Removed Features
Several features were removed without direct replacements:
- Spring Boot/Security adapters (removed in 20+): Use standard OpenID Connect libraries instead
- Legacy admin console (removed in 21): No alternative; the new console is the only option
- WildFly distribution (removed in 20): Must use Quarkus
- Certain client configuration options: Some legacy OAuth 1.0 and SAML settings were deprecated and removed
Pre-Migration Checklist
Before touching any infrastructure, complete this checklist:
1. Full Database Backup
Take a complete, verified backup of your Keycloak database. Test that you can restore it to a separate instance. This is your safety net for every hop.
# PostgreSQL example
pg_dump -h localhost -U keycloak -d keycloak -F c -f keycloak_backup_v8.dump
# Verify the backup restores correctly
createdb keycloak_test
pg_restore -h localhost -U keycloak -d keycloak_test keycloak_backup_v8.dump
2. Document Current Configuration
Export your complete standalone.xml (or equivalent) and note every custom setting. Document environment variables, JVM arguments, and any wrapper scripts.
# Copy your current config
cp /opt/keycloak/standalone/configuration/standalone.xml ./keycloak-config-backup/
# Document environment variables
env | grep -i keycloak > ./keycloak-config-backup/env-vars.txt
3. Inventory Custom Components
List every custom theme, SPI provider, event listener, and authentication flow. For each one, note:
- Source code location
- Which Keycloak APIs it depends on
- Who maintains it
- Whether it has automated tests
4. Review Release Notes
Read the release notes and migration guides for every version between your current and target version. The official Keycloak documentation links to upgrading guides for each release.
5. Set Up a Test Environment
You need an isolated environment that mirrors production. Docker makes this straightforward, and you can use our Docker Compose Generator to create a baseline configuration.
Step-by-Step Migration Process
Repeat this process for each version hop in your migration path.
Step 1: Backup Everything
Before each hop, take fresh backups of the database, configuration files, custom themes, and SPI JARs.
# Create a dated backup directory
BACKUP_DIR="keycloak-migration/backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Backup database
pg_dump -h db-host -U keycloak -d keycloak -F c -f "$BACKUP_DIR/keycloak.dump"
# Backup configuration and custom components
cp -r /opt/keycloak/themes/custom "$BACKUP_DIR/themes/"
cp -r /opt/keycloak/providers "$BACKUP_DIR/providers/"
cp /opt/keycloak/conf/keycloak.conf "$BACKUP_DIR/" 2>/dev/null ||
cp /opt/keycloak/standalone/configuration/standalone.xml "$BACKUP_DIR/"
Step 2: Deploy New Version Alongside Old
Never upgrade in place. Run the new version as a separate instance pointing at a copy of your database.
# Restore database backup to a test database
createdb keycloak_migration_test
pg_restore -h localhost -U keycloak -d keycloak_migration_test "$BACKUP_DIR/keycloak.dump"
Step 3: Start the New Version
Point the new Keycloak instance at your test database and let the schema migration run. Monitor the logs carefully.
# For Quarkus-based versions (17+)
docker run --rm
-e KC_DB=postgres
-e KC_DB_URL=jdbc:postgresql://host.docker.internal:5432/keycloak_migration_test
-e KC_DB_USERNAME=keycloak
-e KC_DB_PASSWORD=secret
-p 8081:8080
quay.io/keycloak/keycloak:21.0.0 start-dev
Watch the startup logs for:
- Liquibase migration output (schema changes being applied)
- Deprecation warnings
- Error messages related to custom providers
- Theme loading issues
Step 4: Test Everything
This is where most teams cut corners, and where most production incidents originate. Test systematically:
Authentication flows:
- Standard username/password login
- Social login providers (Google, GitHub, etc.)
- Multi-factor authentication (OTP, WebAuthn)
- Password reset flow
- Registration flow
- Custom authentication flows
API integrations:
- Admin REST API calls
- Token introspection endpoints
- UserInfo endpoint
- OIDC discovery document
- SAML metadata endpoints
Custom components:
- All custom themes render correctly
- SPI providers load and function
- Event listeners fire and process events
- Custom protocol mappers produce correct token claims
Administrative operations:
- User creation and management
- Realm configuration changes
- Client configuration
- Identity provider settings
Step 5: Check for Deprecation Warnings
Even when everything works, review the logs for deprecation warnings. These indicate features that will break in a future version. Address them now while you are already in migration mode.
# Search logs for deprecation warnings
docker logs keycloak-test 2>&1 | grep -i "deprecat"
Step 6: Repeat for Next Hop
Once you have verified everything works at the current intermediate version, repeat the entire process for the next hop in your migration path.
Docker-Based Test Approach
Docker is the most practical way to test migrations because you can spin up any Keycloak version in seconds without affecting your infrastructure. Here is a compose file for testing a migration hop:
services:
postgres:
image: postgres:15
environment:
POSTGRES_DB: keycloak
POSTGRES_USER: keycloak
POSTGRES_PASSWORD: keycloak_password
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
keycloak-source:
image: quay.io/keycloak/keycloak:17.0.0
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak_password
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
ports:
- "8080:8080"
command: start-dev
depends_on:
- postgres
keycloak-target:
image: quay.io/keycloak/keycloak:21.0.0
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak_password
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
ports:
- "8081:8080"
command: start-dev
profiles:
- target
depends_on:
- postgres
volumes:
pgdata:
Workflow:
- Start with
docker compose up postgres keycloak-source - Configure your realms, users, and clients on the source version
- Stop the source:
docker compose stop keycloak-source - Start the target:
docker compose --profile target up keycloak-target - Verify the migration ran and everything works on port 8081
- Adjust the target image tag for each subsequent hop
This approach lets you iterate quickly without risking production data. If a migration fails, you can reset the database volume and try again.
Common Pitfalls
Running Schema Migration on Production Without Backup
This is the most dangerous mistake. Keycloak’s Liquibase migrations are forward-only. If a migration fails partway through or produces unexpected results, you cannot roll back without a database backup. Always migrate against a copy first, verify, and only then apply to production.
Custom Themes Breaking Due to FreeMarker Changes
Theme templates are not versioned or covered by a stability guarantee. Between major versions, template variables get renamed, new required blocks get added, and HTML structure changes. If you maintain custom themes, budget significant testing time for each hop. Consider using Keycloak’s built-in theme inheritance mechanism rather than copying entire templates, so you only override what you need to change.
Hardcoded WildFly Paths in Scripts
Deployment scripts, health checks, and monitoring tools that reference WildFly-specific paths (/opt/jboss/keycloak/, /subsystem=, jboss-cli.sh) will silently fail on Quarkus-based versions. Audit all automation for WildFly-specific references before migrating to 17+.
Authentication Flow Changes Between Versions
Keycloak has restructured how authentication flows are stored and executed across several versions. Custom authentication flows may behave differently or fail to load. Always export your authentication flow configurations before upgrading and compare them after migration.
Client Scope Changes
Default client scopes have changed between versions. New scopes have been added, and some existing scopes have been modified. Verify that your applications receive the expected token claims after migration.
Ignoring the Intermediate Testing Steps
Teams under time pressure often try to skip intermediate hops or rush through testing. This almost always results in production issues. Each hop exists because the changes between those versions are tested together. Skipping hops means running untested migration paths.
When to Consider Managed Keycloak
If the migration process described above seems daunting, that is because it is. Upgrading Keycloak across major versions is a significant undertaking that requires deep knowledge of the platform, careful planning, and extensive testing. For many teams, it is a multi-week project.
Skycloak handles this entirely for you:
- Automatic upgrades: We test and apply new Keycloak versions so you do not have to plan migration paths
- Zero-downtime rolling upgrades: Your users never experience an authentication outage during upgrades
- Custom component compatibility: We test your themes, SPIs, and extensions against new versions before applying upgrades
- Database migration safety: Automated backups before every upgrade with instant rollback capability
- No WildFly-to-Quarkus pain: New Skycloak instances always run the latest Quarkus-based distribution
If you are currently running a legacy Keycloak version and facing the prospect of a multi-hop migration, it may be more cost-effective to migrate your data to a managed instance rather than performing the upgrade yourself.
Conclusion
Upgrading Keycloak from legacy versions to modern releases is a complex but necessary process. The key principles are:
- Never skip intermediate versions. Follow the recommended hop path: 8 to 12, 12 to 17, 17 to 21, 21 to 24/26.
- Always back up before each hop. Database migrations are irreversible.
- Test exhaustively at each stop. Authentication flows, custom themes, SPIs, and API integrations all need verification.
- Plan for the WildFly-to-Quarkus transition. This is the most significant change and requires rewriting your configuration and deployment automation.
- Use Docker for safe, repeatable testing. Spin up any version in seconds and iterate without risk.
The effort is worth it. Modern Keycloak (21+) is faster, more secure, easier to configure, and actively maintained. Staying on unsupported versions is a security risk that grows with every passing month.
Ready to skip the migration headaches entirely? Try Skycloak and let us handle Keycloak operations, upgrades, and scaling for you. You can also explore our documentation for more guides on Keycloak best practices.
Ready to simplify your authentication?
Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.