Upgrading Keycloak: A Practical Migration Guide from Legacy to Modern Versions

Guilliano Molaire Guilliano Molaire Updated March 13, 2026 12 min read

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.xml settings to keycloak.conf format
  • 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:

  1. Start with docker compose up postgres keycloak-source
  2. Configure your realms, users, and clients on the source version
  3. Stop the source: docker compose stop keycloak-source
  4. Start the target: docker compose --profile target up keycloak-target
  5. Verify the migration ran and everything works on port 8081
  6. 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:

  1. Never skip intermediate versions. Follow the recommended hop path: 8 to 12, 12 to 17, 17 to 21, 21 to 24/26.
  2. Always back up before each hop. Database migrations are irreversible.
  3. Test exhaustively at each stop. Authentication flows, custom themes, SPIs, and API integrations all need verification.
  4. Plan for the WildFly-to-Quarkus transition. This is the most significant change and requires rewriting your configuration and deployment automation.
  5. 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.

Guilliano Molaire
Written by Guilliano Molaire Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Ready to simplify your authentication?

Deploy production-ready Keycloak in minutes. Unlimited users, flat pricing, no SSO tax.

© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman