Akka Cluster Sharding: When to Distribute Stateful Entities
Decide whether Akka Cluster Sharding fits your stateful actor service, with configuration, an Order entity example, and operational trade-offs.
06 Sept 2026, 03:31 UTC

The problem: messaging an actor you cannot find
Imagine an order service running on a six-node cluster. Each order is a stateful actor that holds a shopping cart, validates transitions, and emits events. A customer service representative needs to send "ship" to order-48291, but the service tier has no idea which JVM hosts that actor. If you hard-code node affinity, adding a node breaks routing. If you broadcast, you waste capacity.
Akka Cluster Sharding addresses this by distributing entity actors across the cluster and letting any node send a message to an entity ID. The shard layer resolves the ID to a node, forwards the message, and starts the actor if it is stopped.
What sharding actually does
Cluster Sharding groups entities into shards. A consistent hash of the entity ID picks a shard, and the shard coordinator assigns each shard to one cluster node. Coordinator state is kept in Akka Distributed Data (ddata), a conflict-free replicated data type store that replicates location information across nodes without an external database.
Each entity lives on exactly one node at a time. Passivation can stop an idle actor to free memory, while the shard remembers that the entity existed. A later message re-creates it on whichever node currently owns the shard. This matters when you have millions of entity IDs but only thousands active at once.
Configuration decisions to make first
- Entity type key. In Akka Typed, define an
EntityTypeKeyfor each entity kind, such asOrderorDevice. Do not mix Classic and Typed sharding in the same cluster; it is unsupported. - Shard count. The default is 1000 shards. Plan for at least 10 times your expected maximum node count, and treat the count as fixed after the cluster forms. Changing it requires a full cluster restart.
- Remember entities. Set
remember-entities = onif the set of active entities must survive a rolling restart. This requires Akka Persistence with a configured journal and snapshot store; without it, restarts trigger a rebalancing storm as entities start on new nodes. - Passivation. Use an idle timeout or custom passivation strategy. Passivation stops the actor but does not persist in-memory state; state must be rebuilt from persisted events on restart.
Worked example: an Order entity
This example assumes Akka Typed on Akka 2.6 or later. Define the entity behavior and shard initialization:
val orderShardRegion: ActorRef[ShardingEnvelope] =
ClusterSharding(system).init(
Entity(orderTypeKey)(createBehavior = entityContext => OrderBehavior(entityContext.entityId))
)
The extractor routes commands by order ID:
val extractEntityId: ShardingEnvelope.ExtractEntityId = {
case ShardingEnvelope(orderId, command) => (orderId, command)
}
Configure sharding in application.conf:
akka.cluster.sharding {
state-store-mode = ddata
remember-entities = off
number-of-shards = 1000
passivation {
strategy = default
default {
idle-timeout = 120s
}
}
}
To verify placement in a running cluster, query the Akka Management HTTP endpoint from any host that can reach the management port:
curl -s http://:/cluster/shards
The response lists shard-to-node mappings. Confirm that expected order IDs land on the intended shards and that no node holds a disproportionate share. Risk: do not expose this endpoint to the public internet without authentication and TLS.
Trade-offs and limitations
- Lookup latency. The first message to an entity may cross the network to reach the shard region, then the entity node. Subsequent messages are often local, but the indirection is not free. Avoid sharding for ultra-low-latency request paths.
- Quorum requirement. The ddata coordinator needs a majority of nodes. In a network partition, the minority side cannot create new entities or rebalance shards.
- Recovery cost. If
remember-entities = onand an entity has a long event history, restarting it requires replaying events or loading snapshots. Misconfigured retention can make recovery slow.
How to decide and verify
Adopt Cluster Sharding when you have many stateful entities, need location transparency, and can tolerate the operational overhead of a clustered JVM service. Skip it if you have few entities, need sub-millisecond messaging, or cannot run a majority-based coordinator.
Before rolling out, start a three-node local cluster, enable akka.cluster.sharding.verbose-debug-logging = on, and watch allocation and passivation messages during node startup and shutdown. Then run a load test against a fixed set of entity IDs and check latency percentiles as you add nodes. If mappings stay balanced and passivation frees memory as expected, the configuration is behaving as designed.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.