Bulkhead
The Bulkhead pattern partitions resources into isolated, bounded pools so overload or failure in one consumer cannot starve the rest of the system. It contains blast radius at the cost of some resource efficiency, complementing circuit breakers and timeouts.
Ships are divided into watertight compartments called bulkheads so that a breach in one section does not flood the whole hull. The Bulkhead pattern applies the same idea to software: partition resources — threads, connections, memory, or service instances — into isolated pools so that the failure or saturation of one consumer cannot starve the others. Without bulkheads, a single slow dependency can absorb every thread in a shared pool and bring down endpoints that have nothing to do with it.
How It Works
Instead of one global pool shared by all callers, the system allocates separate, bounded pools per dependency, per tenant, or per workload class:
- Thread/connection pools: each downstream service gets its own pool with a fixed maximum. When dependency A hangs, only A's pool fills; calls to B keep flowing.
- Semaphore isolation: a lightweight alternative that caps concurrent calls without dedicating threads.
- Process or instance isolation: critical workloads run on separate instances, containers, or clusters so a crash or resource leak is contained.
The key property is bounded blast radius. Each compartment has a hard capacity limit, so saturation is local rather than systemic.
When to Use It
Use bulkheads when one process calls multiple downstream dependencies of differing reliability, when a multi-tenant system must prevent a noisy neighbor from starving others, or when premium and best-effort traffic share infrastructure. They are also valuable for separating critical paths (checkout, auth) from non-critical ones (recommendations, analytics).
Trade-offs
Partitioning reduces resource efficiency: capacity reserved for one pool sits idle when that pool is quiet, even if another pool is starved. Sizing many pools correctly is harder than tuning one, and under-provisioning a pool causes rejections under normal load. Bulkheads add configuration and operational complexity. The benefit is fault containment — a property that a single shared pool, however large, cannot provide.
Related Patterns
Bulkheads complement circuit-breaker (the breaker stops calls to a failing dependency; the bulkhead limits the damage while it fails), timeout-pattern (bounded waits keep a pool from filling), rate-limiter, and load-shedding.
Example
Resilience4j bulkhead config isolating two dependencies:
inventory: maxConcurrentCalls = 25
payments: maxConcurrentCalls = 10
If the inventory service hangs, at most 25 concurrent calls block there; the payments path keeps its 10 slots and stays responsive. Kubernetes achieves a coarser bulkhead by giving each service its own pods with resource limits, so one service's memory leak cannot starve another.