What Is SPI Packaging and Deployment Strategy in Keycloak?
The complete, in-depth guide to building, packaging, and safely shipping custom extensions into a running Keycloak deployment.
Picture a large commercial kitchen built around a standard set of appliance sockets — every oven, mixer, and specialized tool plugs into the exact same style of outlet, regardless of which manufacturer built it or when it was added to the kitchen. A head chef can bring in a brand-new, custom-built appliance mid-service, plug it into an existing socket, and have it working alongside everything else without rewiring the whole kitchen. Keycloak’s own extensibility model, built around Service Provider Interfaces (SPIs), offers exactly this kind of socket: a defined, stable extension point that custom code can plug into. This guide covers the other half of that story — not what an SPI does once it’s running, but the genuinely advanced discipline of building, packaging, and deploying that custom code safely, reliably, and repeatably into a real, production Keycloak deployment.
This guide is a comprehensive, advanced-level exploration of SPI packaging and deployment strategy: how a custom provider is actually structured and registered, the packaging and classloading mechanics unique to Keycloak’s Quarkus-based distribution, the deployment pipeline from a developer’s own build through to a running production cluster, and the real, production-tested patterns that keep custom extensions safe to maintain across Keycloak’s own ongoing upgrades. This guide assumes familiarity with Keycloak’s SPI concept at a conceptual level (as introduced in this series’ Event Listener SPI discussion) and with ordinary Java build tooling, and focuses specifically on the advanced mechanics of packaging and shipping that code.
1Core Concepts
What “SPI” Means, Restated Precisely
A Service Provider Interface (SPI) is a defined extension point — a Java interface Keycloak itself declares — that custom code can implement to plug new behavior into Keycloak without modifying Keycloak’s own core codebase. Examples covered elsewhere in this series include the Event Listener SPI; Keycloak exposes dozens of others, covering Authenticators, User Storage integrations, Protocol Mappers, and more.
Provider vs. Provider Factory
Two related but distinct concepts underpin every SPI implementation: a Provider is the actual object doing the real work for a single request or operation, while a Provider Factory is a longer-lived object responsible for creating Provider instances and holding any shared, expensive-to-initialize configuration. Understanding this split matters directly for the lifecycle and performance discussions later in this guide.
Writing the actual logic inside a custom Authenticator or Event Listener is a separate concern from what this guide focuses on. This guide is specifically about everything that happens after that logic is written — how it gets compiled, packaged, registered, and safely deployed into a real, running, and eventually upgraded Keycloak environment.
Why Packaging and Deployment Deserve Their Own Dedicated Discipline
A custom SPI implementation that works perfectly in a developer’s own local testing can still fail in production for reasons entirely unrelated to its own logic — a classloading conflict, a missing service-registration file, an incompatibility introduced by a routine Keycloak version upgrade. This guide exists because these packaging and deployment concerns are genuinely their own advanced discipline, distinct from and just as important as writing the extension’s actual functional code.
2Architecture & Components
graph TD
Source["Custom Provider Source Code"]
Source --> Build["Build & Package as JAR"]
Build --> Registry["META-INF/services
Registration File"]
Build --> Artifact["Packaged JAR Artifact"]
Artifact --> Providers["Keycloak's providers/
Directory"]
Providers --> KcBuild["kc.sh build
(Quarkus Augmentation)"]
KcBuild --> Runtime["Running Keycloak Instance
With Extension Active"]
Provider JAR
The packaged, compiled implementation of one or more custom SPI providers, along with any dependencies it needs.
META-INF/services File
The mechanism Keycloak uses to actually discover which classes inside a JAR implement which SPI — covered fully in Chapter 4.
providers/ Directory
Where a packaged JAR is physically placed inside a Keycloak installation for it to be picked up at all.
Build (Augmentation)
Keycloak’s Quarkus-based build step, which must be re-run whenever a provider is added, changed, or removed — covered fully in Chapter 7.
3Deep Dive: The Provider and Provider Factory Pattern
Why Keycloak Splits Responsibility This Way
Separating a Provider Factory’s longer-lived responsibilities from a Provider’s per-use work lets Keycloak reuse expensive setup — reading configuration, establishing a connection pool — once per Factory, while still creating a fresh, isolated Provider instance for each individual operation, avoiding both the cost of repeated expensive setup and the risk of unintended state leaking between unrelated requests.
The Factory’s Lifecycle Hooks
A Provider Factory implements specific lifecycle methods Keycloak calls at defined moments — once when the Factory is first initialized, again after every Provider Factory across the whole system has been initialized (letting one Factory safely reference another), and finally when the server shuts down, giving the Factory a chance to release any held resources cleanly.
Production Example: A Provider Factory Holding a Database Connection Pool
A custom User Storage provider integrating with an external, legacy user database typically establishes its connection pool once, inside the Provider Factory’s own initialization, then hands out a lightweight Provider instance per request that simply borrows a connection from that already-established pool — avoiding the real cost of establishing a brand-new database connection on every single login attempt.
4Deep Dive: Service Registration via META-INF/services
The Java ServiceLoader Standard
Keycloak’s provider discovery builds on Java’s own standard ServiceLoader mechanism: a text file, placed at a specific, predictable path inside the packaged JAR (under META-INF/services/, named exactly after the SPI’s own fully qualified interface name), listing the fully qualified class name of every Provider Factory implementation that JAR contains for that specific SPI.
Why a Missing or Misconfigured Registration File Is a Common, Silent Failure
If this registration file is missing, misnamed, or lists an incorrect class name, Keycloak simply never discovers the custom provider at all — with no obvious error pointing directly at the actual cause, since from Keycloak’s own perspective, the provider genuinely doesn’t exist as far as its own discovery mechanism is concerned. This is one of the most common, and most confusing to a newcomer, packaging mistakes covered further in Chapter 15.
The registration file’s own name is not arbitrary — it must exactly match the fully qualified name of the SPI interface being implemented. A single typo here, or registering against a slightly wrong interface (perhaps an older or unrelated one with a similar name), silently prevents Keycloak from ever finding the provider, making this file worth double-checking explicitly whenever a new provider mysteriously fails to appear as expected.
5Deep Dive: Packaging a Single JAR vs. Multiple Providers
The Case for Bundling Related Providers Together
Several closely related providers — a custom Authenticator and a companion Required Action it depends on, for instance — are often packaged into a single JAR, since they’re conceptually one feature, tend to be versioned and deployed together, and sharing a single JAR avoids the overhead of managing several nearly-identical deployment artifacts for what is, in practice, one cohesive unit of functionality.
The Case for Separating Genuinely Independent Providers
Conversely, providers serving entirely unrelated purposes — a custom Event Listener feeding a SIEM pipeline, as covered in this series’ audit-trail guide, and an entirely unrelated custom Authenticator — are often better packaged separately, so each can be versioned, tested, and deployed on its own independent schedule without one’s changes forcing an unrelated redeployment of the other.
Production Example: A Shared “Common” JAR for Reused Utility Code
An organization maintaining several independent custom providers that all share some genuinely common utility code — a shared configuration-loading helper, a shared logging wrapper — often factors that shared code into its own separate JAR, which each provider-specific JAR then depends on, avoiding duplicated code across providers while still keeping each provider’s own deployment genuinely independent.
6Deep Dive: Classloading Isolation
Why Isolation Matters at All
A custom provider frequently depends on its own third-party libraries — a specific HTTP client version, a specific JSON-parsing library — and without deliberate isolation, a version of a library bundled with one provider could silently conflict with a different version of that same library already used internally by Keycloak itself, or by an entirely unrelated, independently developed provider deployed alongside it.
How Keycloak’s Own Classloading Model Addresses This
Keycloak’s Quarkus-based runtime applies a defined classloading strategy specifically designed to isolate provider-supplied classes from Keycloak’s own internal implementation classes, reducing (though not entirely eliminating) the risk of this kind of conflict. Understanding exactly which classes are shared versus isolated, and which specific packages a provider is expected to avoid bundling its own conflicting version of, is essential advanced knowledge for any team building nontrivial custom providers with their own real external dependencies.
A classloading conflict frequently compiles perfectly cleanly and only manifests as an obscure runtime error — a method not found, an unexpected class-cast failure — the first time the conflicting code path is actually exercised in a running server. This makes thorough, realistic integration testing, covered in Chapter 12, especially important for any provider bundling its own nontrivial third-party dependencies.
Shading and Relocation as a Mitigation Technique
One common technique for reducing this risk is shading — repackaging a bundled dependency’s own classes under a renamed, provider-specific package path at build time, so it can never collide with a same-named class Keycloak or another provider happens to load from the shared classpath. This trades a small amount of build complexity for a meaningfully reduced risk of exactly the kind of silent, runtime-only conflict described above, and is worth adopting deliberately for any dependency known to be widely used and therefore prone to version clashes.
Preferring Keycloak’s Own Already-Provided Libraries Where Possible
Before bundling a provider’s own copy of a common library, it’s worth checking whether Keycloak’s own runtime already provides a compatible version on the shared classpath — depending on that already-provided version, rather than bundling a separate, potentially conflicting copy, sidesteps the entire class of problem this chapter describes for that specific dependency, at the cost of a tighter coupling to whichever version Keycloak itself happens to ship in a given release.
7Deep Dive: The Quarkus Build (Augmentation) Step
What Augmentation Actually Does
Keycloak’s modern, Quarkus-based distribution performs a dedicated build step (commonly invoked through its own build command) that inspects the current set of providers present in the installation and pre-computes significant parts of the server’s own startup configuration ahead of time, trading a small amount of upfront build time for meaningfully faster actual server startup afterward.
Why a New or Changed Provider Requires Re-Running This Step
Because this build step bakes the current provider set into its precomputed configuration, simply dropping a new JAR into the providers directory isn’t, on its own, sufficient — the build step needs to be explicitly re-run afterward for the newly added (or changed, or removed) provider to actually take effect, a detail that surprises teams accustomed to older, purely dynamic deployment models where a dropped-in file might have taken effect immediately.
Production Example: A Deployment Pipeline That Forgets the Build Step
A team automating provider deployment through a script that copies a new JAR into the providers directory but omits the explicit build step can end up with a server that starts successfully, shows no obvious error, and yet simply doesn’t have the new provider active at all — precisely the kind of silent, confusing failure this guide’s packaging and deployment discipline exists to prevent through a properly complete, tested pipeline.
Optimized vs. Non-Optimized Startup Modes
Keycloak’s Quarkus-based distribution offers both a fully pre-built, optimized startup mode (where the build step has already run ahead of time, as this guide generally assumes) and a non-optimized mode that performs an equivalent of the build step automatically at every single startup instead. The non-optimized mode is genuinely convenient during early, local development, where providers change frequently and re-running a separate build command each time would be tedious — but it comes at the cost of meaningfully slower startup, making it generally unsuitable for a production deployment where fast, predictable startup time actually matters.
Verifying the Build Actually Picked Up the Intended Providers
Beyond simply remembering to run the build step, it’s worth explicitly confirming afterward that it genuinely detected and incorporated the intended provider set — Keycloak’s own build output typically lists the providers it discovered, and reviewing this output as a deliberate, explicit verification step, rather than simply assuming success from the absence of an error, closes the loop on exactly the kind of silent failure this chapter has been focused on throughout its discussion.
8Internal Working: The Full Startup Sequence With Custom Providers
JAR Discovered in the providers/ Directory
Keycloak’s build tooling scans this directory as part of the augmentation step covered in Chapter 7.
Service Registration Files Read
Each JAR’s META-INF/services entries, covered in Chapter 4, are read to determine which SPIs this JAR provides implementations for.
Provider Factories Instantiated
Each discovered Provider Factory class is instantiated and its own initialization lifecycle hooks, covered in Chapter 3, are invoked.
Cross-Provider References Resolved
The post-initialization lifecycle hook runs, letting any Provider Factory safely reference another Factory now that every Factory across the whole system has completed its own basic setup.
Server Ready, Providers Active
The server finishes startup, and every registered custom provider is now available to be selected and used within a Realm’s own configuration, exactly like any of Keycloak’s own built-in providers.
9Data Flow & Lifecycle
Development. A developer writes and locally tests the provider’s own implementation, typically against a local Keycloak instance.
Build and Package. The provider is compiled and packaged into a JAR, including its own service-registration file and any bundled dependencies, as covered in Chapters 4 through 6.
Staged Deployment. The packaged JAR moves through the organization’s own deployment pipeline (covered further in Chapter 11), typically reaching a staging environment before production.
Production Activation. The JAR is placed into production’s providers directory, the build (augmentation) step is re-run, and the server is restarted or rolled, activating the new or updated provider.
Ongoing Operation. The provider runs in production, ideally monitored using the practices covered in Chapter 14.
Version Compatibility Review. Whenever the underlying Keycloak version itself is upgraded, the provider needs explicit review against that new version, as covered in Chapter 13, before the upgrade proceeds to production.
graph LR
A["Development"] --> B["Build & Package"]
B --> C["Staged Deployment"]
C --> D["Production Activation"]
D --> E["Ongoing Operation"]
E --> F["Keycloak Upgrade
Compatibility Review"]
F --> B
10Deep Dive: Configuration and Externalizing Provider Settings
Provider-Specific Configuration via the SPI Framework
Keycloak’s own SPI framework supports defining configuration properties directly for a custom provider, exposed through the Admin Console alongside Keycloak’s own built-in provider configuration, letting an administrator adjust a custom provider’s behavior without needing to touch its underlying code or redeploy anything.
Externalizing Genuinely Sensitive or Environment-Specific Settings
For values that shouldn’t be hard-coded or stored directly inside Keycloak’s own configuration store — a third-party API key, an environment-specific connection string — a well-designed provider generally reads these from an external source, such as an environment variable or a dedicated secrets-management system, following the same principle covered in this series’ Realm Export/Import guide regarding never embedding real secrets directly where they’d be broadly visible.
Production Example: Environment-Specific Behavior From a Single JAR
A single, identically packaged provider JAR can behave differently in staging versus production purely by reading different environment variables at startup, letting one build artifact travel unchanged through every stage of the deployment pipeline, with only its externalized configuration — never its actual packaged code — differing between environments.
11Deep Dive: A CI/CD Pipeline for Custom Providers
Automated Build and Test
A sound pipeline compiles the provider, runs its own unit tests, and ideally runs an integration test against a genuinely running Keycloak instance (covered further in Chapter 12) automatically on every change, catching a broken build or a regression well before it ever reaches a shared environment.
Versioned, Immutable Artifacts
Each successful build produces a versioned, immutable JAR artifact, stored in an artifact repository, so any specific version can be reliably retrieved again later — for a rollback, for an audit, or simply to confirm exactly which version is currently running in a given environment.
Staged Promotion Through Environments
The same, unchanged artifact is promoted through successive environments — development, staging, production — rather than being rebuilt separately for each one, ensuring the exact code that was tested in staging is genuinely, bit-for-bit the same code that eventually reaches production.
Given Chapter 7’s discussion of the required build (augmentation) step, a genuinely complete deployment pipeline automates that step explicitly as part of the deployment process itself, rather than relying on a manual, easily forgotten step performed by whoever happens to be deploying that day.
12Deep Dive: Testing Strategies for Custom Providers
Unit Testing the Provider’s Own Logic
Ordinary unit tests, exercising a provider’s own business logic in isolation, remain valuable and fast to run, but they generally cannot catch packaging-level issues — a missing service-registration file, a classloading conflict — since they never actually exercise Keycloak’s own real provider-discovery and startup machinery.
Integration Testing Against a Real, Running Keycloak Instance
A genuinely thorough testing strategy includes spinning up an actual, real Keycloak instance (often in a disposable container) with the packaged provider JAR actually installed, and exercising it exactly as a real user or administrator would — the only way to reliably catch the packaging-specific failure modes covered throughout this guide before they reach a shared environment.
Production Example: A Containerized Integration Test Suite
A mature team maintains an automated test suite that builds the provider JAR, starts a fresh, disposable Keycloak container with that exact JAR installed and the build step properly run, and then runs a realistic scenario against it — confirming not just that the provider’s own logic is correct, but that it’s genuinely discoverable, loadable, and usable inside a real, running server exactly as production would experience it.
13Deep Dive: Compatibility Across Keycloak Version Upgrades
SPIs Can Change Between Keycloak Versions
Because SPIs are part of Keycloak’s own internal extension surface rather than a permanently frozen public contract, a given SPI’s interface can change — a new method added, a signature altered — between Keycloak versions, meaning a custom provider built against one version isn’t automatically guaranteed to compile, or behave correctly, against a later one.
Why This Demands a Deliberate, Tested Upgrade Process
Upgrading the underlying Keycloak version without also reviewing and, where necessary, updating every custom provider against that new version risks a provider silently failing to build, failing to load, or — more subtly and dangerously — building and loading successfully while behaving incorrectly against a changed underlying contract. A sound upgrade process treats every custom provider as something that must be explicitly re-verified against a new Keycloak version, never assumed to simply continue working unchanged.
An organization maintaining more than a small handful of custom providers benefits genuinely from tracking, in one explicit, shared place, which version of each provider has actually been tested and verified against which version of Keycloak — turning what would otherwise be tribal knowledge into a documented, reviewable record that directly informs every future upgrade decision.
Reading Keycloak’s Own Release Notes for SPI-Level Changes
Before upgrading, a deliberate review of Keycloak’s own release notes specifically for changes to the SPIs a custom provider actually implements is well worth the modest time it takes — these notes generally call out breaking changes to extension points explicitly, and catching a relevant change here, ahead of time, is considerably cheaper than discovering it only after an upgrade has already reached a shared or production environment where far more people are affected by any resulting disruption.
Maintaining a Dedicated Upgrade-Testing Environment
A particularly disciplined approach maintains a separate environment specifically for trialing an upcoming Keycloak version against the full, current set of custom providers before that version is approved for wider promotion — catching any compatibility issue in a low-stakes setting deliberately built for exactly this purpose, rather than discovering it for the first time during a genuine staging or production upgrade, when the cost of an unexpected surprise is considerably higher.
Pinning Dependency Versions Explicitly Rather Than Floating
A custom provider’s own build configuration should generally pin its dependency versions — including its dependency on Keycloak’s own SPI interfaces themselves — to specific, known values rather than allowing them to float to whatever the latest available version happens to be at build time. A floating dependency can silently introduce an incompatibility on a routine rebuild, entirely independent of any deliberate Keycloak upgrade decision, making the provider’s own behavior effectively nondeterministic across builds taken at different points in time, and considerably harder to reason about during any later troubleshooting effort.
14Monitoring, Logging & Metrics for Custom Providers
Provider Registration Confirmation
Explicitly verifying, on every deployment, that a custom provider was genuinely discovered and registered — not silently missing due to the failure modes covered in Chapters 4 and 7.
Provider-Specific Latency
Tracking how much time a custom provider itself adds to whatever flow it participates in, isolating its contribution from Keycloak’s own baseline performance.
Provider-Level Exception Logging
Ensuring a custom provider’s own exceptions are clearly attributable to that specific provider in logs, rather than blending indistinguishably into Keycloak’s own general error output.
Version Drift Alerts
Flagging, ideally automatically, whenever a provider’s own tracked compatibility (from Chapter 13’s matrix) falls behind the actual Keycloak version currently running in production.
A Provider Should Fail Loudly, Not Silently
Given how many of this guide’s failure modes are inherently silent — a missing registration file, a forgotten build step — a well-built custom provider deliberately logs a clear, unambiguous statement of its own successful initialization at startup, giving operators a concrete, positive signal to check for and a fast, direct way to confirm the provider actually came up correctly, rather than needing to infer its presence indirectly.
Exposing a Provider’s Own Version Through a Health or Diagnostics Endpoint
Beyond a simple startup log line, a genuinely mature provider can expose its own version and basic health status through a diagnostics endpoint or Keycloak’s own health-check integration, letting an automated deployment pipeline (or a human operator) programmatically confirm exactly which provider version is currently active in a given environment, rather than relying purely on manual log inspection or, worse, an unverified assumption based on what was supposed to have been deployed.
Correlating Provider Behavior With the Audit Trail
Where a custom provider performs a security-relevant action — a custom Authenticator making an access decision, for instance — its own internal logging benefits from using the same correlation-ID approach covered in this series’ audit-trail guide, letting a provider’s own detailed internal behavior be traced alongside the broader Login and Admin Event stream during an investigation, rather than existing as an isolated, hard-to-correlate log source of its own.
15Design Patterns & Anti-patterns
Good Patterns
- Automate the entire deployment pipeline, including the build (augmentation) step, rather than relying on a manually performed, easily forgotten sequence
- Test against a genuinely running Keycloak instance, not just in isolated unit tests, to catch packaging and classloading issues before they reach production
- Externalize environment-specific and sensitive configuration, keeping one identical, versioned artifact usable unchanged across every environment
- Maintain an explicit provider-to-Keycloak-version compatibility record, reviewed deliberately before every Keycloak upgrade
Anti-patterns to Avoid
Common Mistakes
- Dropping a new provider JAR into production without re-running the required build (augmentation) step
- Getting the META-INF/services registration file name or contents even slightly wrong, causing a silent, undetected registration failure
- Bundling a conflicting version of a library Keycloak already uses internally, without understanding the classloading isolation model from Chapter 6
- Upgrading Keycloak’s own version without explicitly re-verifying every custom provider against the new version first
16Best Practices & Common Mistakes
| Practice | Why It Matters |
|---|---|
| Verify the META-INF/services file name and contents explicitly | A single mismatch here silently prevents provider discovery entirely |
| Include the build (augmentation) step in every automated deployment | A dropped-in JAR alone does not activate a new or changed provider |
| Run integration tests against a real, running Keycloak instance | Packaging and classloading issues rarely surface in isolated unit tests |
| Log a clear, positive confirmation of successful provider startup | Turns a class of inherently silent failures into something directly observable |
| Review every custom provider explicitly before each Keycloak upgrade | SPIs are not a permanently frozen contract and can change between versions |
The Single Most Common Mistake: Treating Deployment as an Afterthought
Teams frequently invest significant, careful engineering effort into a custom provider’s own business logic while treating its packaging and deployment as a quick, informal afterthought — exactly backwards from the genuine risk profile this guide has covered throughout, where packaging and deployment failure modes are disproportionately silent, confusing, and costly relative to the comparatively visible, easily-tested failures in the provider’s own core logic.
17Real-World & Industry Examples
Financial Institutions Integrating Legacy User Directories
A bank maintaining a custom User Storage provider bridging Keycloak to a decades-old, proprietary internal directory typically invests heavily in the compatibility-matrix discipline from Chapter 13, given how directly a provider failure here would disrupt every employee and customer login across the entire organization.
Large SaaS Platforms With Custom Authentication Flows
A SaaS platform building a custom Authenticator implementing a proprietary risk-scoring step, layered into the Authentication Flows covered elsewhere in this series, generally maintains a full, automated CI/CD pipeline and containerized integration-testing setup, given how frequently this kind of provider evolves alongside the platform’s own broader product roadmap.
Regulated Industries With Custom Audit Event Listeners
An organization operating the custom Event Listener SPI implementation covered in this series’ audit-trail guide treats its packaging and deployment with particular rigor, since a silently failed deployment of this specific provider would mean security-relevant events simply stop flowing to the SIEM without any obvious, immediate warning.
Platform Teams Serving Many Internal Product Teams
A platform team responsible for Keycloak on behalf of many separate internal product teams often builds a small internal library of shared utility code, packaged as its own separate JAR following the pattern from Chapter 5, letting each product team’s own custom provider depend on well-tested, centrally maintained common functionality rather than each team separately reimplementing similar logic.
18Deployment & Cloud Considerations
Baking Providers Into a Custom Container Image
Rather than mounting the providers directory as an external volume and copying JARs in at deploy time, many containerized deployments instead bake the provider JARs, and the results of the build (augmentation) step, directly into a custom Keycloak container image itself — producing one immutable, fully self-contained artifact that starts up quickly and consistently, without any separate, additional deployment step needed once the container is running.
Rolling Deployments and Provider Version Consistency
In a clustered deployment, as covered in this series’ clustering guide, a rolling deployment strategy needs to ensure every Node runs the exact same provider version at the same time — a cluster with some Nodes running an old provider version and others running a new one simultaneously can produce genuinely inconsistent behavior depending on which specific Node happens to handle a given request, a scenario worth explicitly avoiding through a deliberate, coordinated rollout strategy.
Production Example: An Immutable, Versioned Container Image per Release
A mature deployment pipeline builds one complete, versioned container image containing Keycloak itself alongside every currently approved custom provider, already built and augmented, and deploys that exact same image uniformly across every Node in the cluster — eliminating the mixed-version risk described above entirely, since there is never a moment where different Nodes are running genuinely different provider code.
Coordinating Provider Rollouts With Stateless Clustering’s Own Rolling-Update Behavior
As covered in this series’ Stateless Clustering guide, a well-configured cluster can tolerate individual Nodes being cycled without disrupting active user sessions. That same rolling-update mechanism is exactly what carries a new provider image out across the cluster during a deployment, but it does mean there is necessarily a brief transition window during which old and new Nodes coexist unless the rollout strategy is deliberately configured otherwise — worth explicitly accepting as a bounded, brief, and monitored transition rather than assuming provider version consistency is instantaneous across every Node at every single moment.
Rollback Readiness as Part of the Same Strategy
Because a new provider version can occasionally introduce an unexpected regression only visible under real production traffic, the same immutable, versioned image approach that enables a clean forward rollout also enables a clean, fast rollback — simply redeploying the previous known-good image — provided the pipeline retains and can quickly retrieve prior versions rather than only ever keeping the single, most recent build readily available, and provided that rollback path has itself been rehearsed at least once before it’s actually needed during a genuine incident.
19Frequently Asked Questions
20Summary and Key Takeaways
Key Takeaways
- Every custom provider follows the Provider / Provider Factory pattern, separating longer-lived, shared setup from lightweight, per-operation work.
- Keycloak discovers a custom provider through a META-INF/services registration file, whose exact naming and contents must match the target SPI precisely — a common, entirely silent point of failure.
- Keycloak’s Quarkus-based build (augmentation) step must be explicitly re-run whenever a provider is added, changed, or removed — simply placing a JAR in the providers directory is not sufficient on its own.
- Classloading isolation is a genuinely advanced concern for any provider bundling its own third-party dependencies, and conflicts frequently surface only at runtime.
- A custom provider is not automatically compatible with a new Keycloak version — SPIs can change between versions, requiring explicit, deliberate review before every upgrade.
- Treat custom providers with the same engineering rigor as any other production software — an automated CI/CD pipeline, integration testing against a real running instance, and externalized, environment-specific configuration.
- In a cluster, ensure every Node runs the exact same provider version simultaneously, ideally through one immutable, versioned image, to avoid inconsistent, Node-dependent behavior.
- Use shading to reduce the risk of a bundled dependency colliding with a same-named class already present on Keycloak’s own shared classpath.
- Retain the ability to quickly roll back to a previous, known-good provider image, not just roll forward — a regression that only appears under real production traffic is a realistic scenario this capability is meant to cover.
Taken together, the discipline covered in this guide reflects a simple underlying idea: a custom provider’s own business logic is only as reliable as the packaging, discovery, and deployment machinery that gets it safely into a running server in the first place. Treating that machinery with the same seriousness as the logic itself — automated, tested, versioned, and reviewed deliberately across every Keycloak upgrade — is what turns a custom Keycloak extension from a fragile, occasionally working add-on into genuinely dependable production infrastructure.