Library / SDK
resilience4j/resilience4j avatar
resilience4j/resilience4j

Resilience4j: A Fault Tolerance Library for Java That You Assemble Yourself

Resilience4j is a fault tolerance library designed for Java8 and functional programming

10,768 stars1,512 forksJavaApache-2.0

At a glance

What is it?
Resilience4j supplies circuit breakers, rate limiters, retries, bulkheads and time limiters as composable decorators rather than a single framework. This review covers the module layout, a first setup, and the cases where it is the wrong choice.
Who is it for?
Adopt Resilience4j when you want fault tolerance primitives that you compose yourself, especially on Java 21 with Spring Boot, Micrometer or Reactor already in the stack. Do not adopt it expecting a single drop-in annotation that handles everything, and do not adopt it if you need a non-Java runtime.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Resilience4j solves, and who ends up using it

A Java service that calls another service over the network inherits that service's failure modes. Calls hang, calls fail intermittently, calls succeed but slowly, and a struggling downstream can consume every thread you have. Resilience4j addresses that class of problem. Its README describes it as a lightweight fault tolerance library designed for functional programming that provides higher-order functions, called decorators, to enhance any functional interface, lambda expression or method reference with a Circuit Breaker, Rate Limiter, Retry or Bulkhead.

The intended audience is narrower than "anyone writing Java." Because the core API is built around functional interfaces and lambdas, it fits codebases already comfortable with Supplier, CompletableFuture and compositional style. Teams that prefer declarative configuration over explicit decoration are served by the Spring Boot and Micronaut modules, which the repository ships as resilience4j-spring-boot3, resilience4j-spring-boot4, resilience4j-spring6 and resilience4j-micronaut. The README frames the design as opt-in: you have the choice to select the decorators you need and nothing else. That is a real architectural decision, not marketing. There is no single Resilience4j object that wraps your application. You pick modules and wire them.

Decorators, registries and the module split

The mechanism is composition. Each module exposes an object you create with a factory such as CircuitBreaker.ofDefaults("backendService"), and Decorators.ofSupplier(...) chains those objects around a Supplier. The README's example stacks a circuit breaker, a bulkhead and a retry on one supplier, then wraps the result in Try.ofSupplier(...).recover(...) so that a failure becomes a fallback value instead of an exception. The decorated object is still a Supplier, so it can be passed anywhere a Supplier is accepted.

The repository is organized as one Gradle module per concern: resilience4j-circuitbreaker, resilience4j-ratelimiter, resilience4j-bulkhead, resilience4j-retry, resilience4j-timelimiter and resilience4j-cache, with resilience4j-all as the aggregate artifact and resilience4j-bom for dependency management. Add-ons cover metrics, Feign, Kotlin, Spring, Ratpack, Vertx, RxJava2 and more, per the README. Two details in the README deserve attention. First, the README states that Resilience4j 3 requires Java 21, while the repository's own Dockerfile installs Java 11.0.23-tem, so the development container and the documented runtime requirement are not aligned. Second, the README's own example notes that Decorators requires the resilience4j-all dependency, which is easy to miss if you added only resilience4j-circuitbreaker.

Instances are keyed by ID. The README's best practices section states a golden rule: create a unique instance with a unique ID for each protected remote service or backend you communicate with. It gives three reasons: per-instance metrics, isolation so that a failure in Service A does not affect patterns protecting Service B, and independent configuration because different backends may need different thresholds. It also distinguishes instance-aware patterns, which must not be shared, from others. That distinction is the single most common source of confusing dashboards, because sharing one circuit breaker across two backends makes the breaker's state describe neither of them.

Adding Resilience4j to a Maven build and decorating a call

The README points to Maven Central for artifacts and to the User Guide at resilience4j.readme.io for setup. The README's own example is the shortest path to a working call: build the patterns with ofDefaults, decorate the supplier, then recover.

java
// Create a CircuitBreaker with default configuration
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendService");

// Create a Retry with default configuration
// 3 retry attempts and a fixed time interval between retries of 500ms
Retry retry = Retry.ofDefaults("backendService");

// Create a Bulkhead with default configuration
Bulkhead bulkhead = Bulkhead.ofDefaults("backendService");

Next, wrap the call itself. The README notes in a comment that you will need the resilience4j-all dependency for this builder, which is the detail most likely to trip up a first build.

java
Supplier<String> supplier = () -> backendService
  .doSomething(param1, param2);

// Decorate your call to backendService.doSomething()
// with a Bulkhead, CircuitBreaker and Retry
// **note: you will need the resilience4j-all dependency for this
Supplier<String> decoratedSupplier = Decorators.ofSupplier(supplier)
  .withCircuitBreaker(circuitBreaker)
  .withBulkhead(bulkhead)
  .withRetry(retry)
  .decorate();

Finally, execute the decorated supplier and turn a failure into a value.

java
String result = Try.ofSupplier(decoratedSupplier)
  .recover(throwable -> "Hello from Recovery").get();

What you should see is a String. When the backend succeeds, it is the backend's value. When it fails, it is the recovery string, and the circuit breaker records the failure. If Decorators cannot be resolved, the README's note applies: add resilience4j-all. For asynchronous calls, the README shows a ThreadPoolBulkhead combined with a TimeLimiter, and states that the Scheduler is needed to schedule a timeout on a non-blocking CompletableFuture.

java
ThreadPoolBulkhead threadPoolBulkhead = ThreadPoolBulkhead
  .ofDefaults("backendService");

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
TimeLimiter timeLimiter = TimeLimiter.of(Duration.ofSeconds(1));

Where the design costs you: shared instances, missing modules and undocumented rollback

The most consequential limitation is conceptual rather than technical. Resilience4j gives you primitives and expects you to decide how they are scoped. The README devotes an entire best practices subsection to instance management because newcomers get it wrong, and the failure is silent. Share one circuit breaker between two backends and both the metrics and the state stop meaning anything useful. Nothing in the library prevents that.

The second limitation is the module surface. Because the project is split across dozens of artifacts, the answer to "which dependency do I add" depends on what you are doing. The README lists core modules and mentions add-ons but explicitly defers the full list to the User Guide, so the README alone is not enough to plan a build. The repository also contains modules that the README does not enumerate at all, including resilience4j-hedge, resilience4j-vavr, resilience4j-reactor, resilience4j-rxjava3 and resilience4j-circularbuffer. Their presence in the tree does not tell you their maturity or intended audience.

The third limitation is that the README does not document rollback or migration. There is no section on downgrading between major versions, and the README's statement that Resilience4j 3 requires Java 21 means a version bump can be a JDK bump. The README is also silent on how long a shared instance should live, on what happens to in-flight calls when a circuit breaker opens, and on the operational procedure for changing thresholds at runtime. Those gaps matter to anyone running this in production, and the User Guide is the place to look rather than the repository README.

Resilience4j versus Hystrix and Spring Retry

The comparison people search for most is against Hystrix, and the architectural difference is the reason the comparison exists. Hystrix was built as a framework that owned the execution path: commands were wrapped in a HystrixCommand and the library controlled threading, isolation and fallback as part of that wrapper. Resilience4j inverts that. Its decorators wrap a Supplier and return a Supplier, so the library never owns your threading model unless you explicitly choose ThreadPoolBulkhead. The README's framing, that you do not have to go all-in and can pick what you need, is a direct statement of that inversion. For a codebase that already has its own async plumbing, that is the difference between adopting a library and restructuring around one.

Against Spring Retry the split is about scope rather than style. Spring Retry addresses retrying, and its annotations integrate with Spring's own retry abstraction. Resilience4j also ships resilience4j-retry, but the retry module sits alongside circuit breaking, rate limiting, bulkheading and time limiting, and the point of the composition is that these interact. A retry that fires against an open circuit breaker is wasted work, and stacking the decorators is how you avoid it. If retrying is the only thing you need and you are already in Spring, adding a single-purpose library is a smaller commitment than adopting the module set. If you need a circuit breaker and a rate limiter as well, the two libraries are not really substitutes.

Maintenance, releases and what the Apache-2.0 licence means here

The repository is not archived, and the last push was on 2026-08-31. Release cadence is visible in the recent tags: v2.4.0 on 2026-03-14, v2.3.0 on 2025-01-03, and v2.2.0 on 2023-12-17. The gap between v2.2.0 and v2.3.0 was roughly a year, and the gap between v2.3.0 and v2.4.0 was about fourteen months. That is a slow but not abandoned rhythm, and it means you should not expect a fix for a specific issue to land in the next few weeks.

Upgrade cost is dominated by the Java version requirement rather than by API churn. The README states that Resilience4j 3 requires Java 21, so moving from the 2.x line to 3.x is a runtime decision before it is a library decision. The repository also carries separate Spring Boot modules for different generations (resilience4j-spring-boot3, resilience4j-spring-boot4, resilience4j-spring6), which means a Spring upgrade can require an artifact change rather than a version bump.

The licence is Apache-2.0, per the LICENSE.txt file and the README's badge. That is a permissive licence, which in practice means you can use the library in closed-source products and modify it, provided you meet the notice and attribution conditions that the licence text spells out. This is not legal advice, and the terms that apply to your distribution are the ones in the LICENSE.txt file in the repository.

One maintenance detail worth flagging: the repository's Dockerfile pins Java 11.0.23-tem via SDKMAN, while the README says Resilience4j 3 requires Java 21. If you build the project from source using that container as your starting point, expect to adjust the JDK yourself.

Editorial conclusion

Adopt Resilience4j when you want fault tolerance primitives that you compose yourself, especially on Java 21 with Spring Boot, Micrometer or Reactor already in the stack. Do not adopt it expecting a single drop-in annotation that handles everything, and do not adopt it if you need a non-Java runtime. Before committing, verify which modules your build actually resolves, check the resilience4j-test module for the test helpers you will need, and confirm that your Spring Boot generation maps to resilience4j-spring-boot3 or resilience4j-spring-boot4 rather than the older resilience4j-spring6 artifact.

Frequently asked questions

What is Resilience4j used for?

It is a fault tolerance library for Java that adds circuit breaking, rate limiting, retrying, bulkheading and time limiting to calls you make to other services. The README describes it as providing decorators that enhance any functional interface, lambda expression or method reference.

What is the difference between Resilience4j and Hystrix?

Resilience4j wraps a Supplier with decorators and leaves the threading model to you, while Hystrix owned the execution path through its command wrapper. The README makes the opt-in design explicit: you select the decorators you need and nothing else.

What is bulkhead in Resilience4j?

Bulkheading is one of the core modules, shipped as resilience4j-bulkhead, and it limits concurrent calls. The README also shows a ThreadPoolBulkhead for asynchronous execution, used together with a TimeLimiter that requires a ScheduledExecutorService.

How do I add Resilience4j to a Spring Boot project?

The repository ships resilience4j-spring-boot3, resilience4j-spring-boot4 and resilience4j-spring6 as separate modules, so the artifact depends on your Spring Boot generation. The README defers full setup instructions to the User Guide at resilience4j.readme.io.

What is the difference between Resilience4j and Spring Retry?

Spring Retry addresses retrying and integrates with Spring's own retry abstraction. Resilience4j also ships resilience4j-retry, but the retry module sits alongside circuit breaking, rate limiting, bulkheading and time limiting, and the decorators are meant to be composed together.

How do I use Resilience4j retry?

Create a Retry with Retry.ofDefaults("backendService"), which the README says gives 3 retry attempts and a fixed time interval between retries of 500ms, then attach it with Decorators.ofSupplier(...).withRetry(retry).

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. resilience4j/resilience4j on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/resilience4j-resilience4j.svg)](https://hysenlabs.com/projects/resilience4j-resilience4j)