Elasticsearch Sharding Architecture
How two decisions, splitting an index into shards and replicating those shards, produce Elasticsearch's scalability and availability.
Contents
Two decisions, splitting an index into shards and replicating those shards, together produce Elasticsearch's scalability and its availability.
Why an index is split into shards
In Elasticsearch, an index is a logical collection of documents, corresponding to a table in a relational database. Once data grows to terabyte (TB) or petabyte (PB) scale, it exceeds the storage and I/O bandwidth of a single server. An index is therefore split into independent pieces called shards and distributed across the cluster.
A shard is both the basic unit of data distribution and a self-contained Apache Lucene index instance. Physically, that Lucene index is itself made up of multiple segments. Every time documents are indexed a new segment is written to disk, and a background merge consolidates small segments into larger ones.
This structure has a physical ceiling. Because of an internal integer identifier limit, a single Lucene index can hold at most 2,147,483,519 documents (a structural limit of the Lucene index). Spreading data across shards rather than piling it into one is therefore where capacity planning starts.
Primary shards and replica shards
In a distributed system, node failure can happen at any moment. Elasticsearch secures availability by creating replica shards, exact copies of the original primary shards. Replicas are scheduled so they never land on the same physical node, so when one server dies a replica on another node is promoted to primary.
A replica is both a failover copy and a share of the read load. Read requests such as searches are served in parallel by primaries and replicas. Adding replicas therefore increases concurrent search throughput as well.
| Property | Primary shard | Replica shard |
|---|---|---|
| Responsibility | Stores the original data | Copy, spreads read load |
| Changing the count | Fixed once the index is created | Adjustable at runtime |
| Writes | Performed directly | Replicated from the primary |
| Placement rule | Distributed across nodes | On a different node from its primary |
The point of the table is that the two shard types differ in mutability. The replica count can be raised or lowered freely at runtime to match read load. The primary count, on the other hand, is fixed when the index is created, and the reason lies in the routing described next.
How a document finds its shard
Which shard a given document lands on is decided by a routing formula. This is where the coupling between the primary count and the hash function becomes visible.
shard = hash(routing) % number_of_primary_shardsThe routing value defaults to the document's _id. Hashing it and taking the remainder modulo the primary count gives the destination shard. Because the modulo depends directly on the primary count, changing that count would send existing documents to the wrong destination. That is why the primary count cannot be changed after creation.
Search works in the opposite direction. A coordinating node scatters the query to the relevant shards and gathers the partial results from each into the final response.
The diagram shows a single search fanning out to several shards and being reassembled on one node. Indexing picks exactly one shard through routing, while search collects from many. That asymmetry is why shard design affects both the read and the write path.
The order in which writes are replicated
Unlike reads, writes always go through the primary first. The coordinating node picks the destination primary by routing, and the primary indexes into its own shard first. It then replicates the same data to the replicas, and once replication is acknowledged it responds to the client with success.
The diagram traces the path a write takes from primary to replicas (standard Elasticsearch replication behavior). More replicas therefore means a single write propagates to more nodes and costs more. Read throughput and write cost are coupled through the replica count.
Separating node roles
A cluster is a group of nodes bound into one logical system. Each node does different work depending on the roles assigned to it.
| Node role | Responsibility |
|---|---|
| Master node | Cluster state and metadata, shard allocation and relocation |
| Data node | Stores documents and inverted indexes, handles search, aggregation, and indexing |
| Ingest node | Transforms, enriches, and filters documents just before indexing |
| Coordinating node | Distributes requests and merges responses, acting as a load balancer |
The master node controls shard allocation and cluster state, so isolating it from data I/O load is more stable. Data nodes take on CPU- and memory-heavy work such as search and aggregation. In small environments a single node can hold several roles, but in production, separating master load from data load makes the cluster more resilient.
Choosing the number of shards
Too many shards is a problem, and so is too few. Too few and a single shard grows enormous; too many and management overhead balloons.
The industry norm in enterprise environments is to keep a single shard in the tens of gigabytes, roughly 30-50GB (per Elastic's recommendation). For example, placing 300GB of data on 10 data nodes with 10 primaries gives each node one evenly distributed 30GB shard.
That decision is expressed as two settings at index creation time. number_of_shards is the primary count and cannot be changed afterward; number_of_replicas can be updated at runtime.
PUT /logs-2026-06
{
"settings": {
"number_of_shards": 10,
"number_of_replicas": 1
}
}Setting replicas to 1 adds 10 replicas to the 10 primaries for 20 shards in total. The replica value can be raised or lowered later with PUT /logs-2026-06/_settings (standard Elasticsearch index settings).
The failure mode in the opposite direction is shard explosion. Creating a fresh index every day for a small amount of data, and never cleaning up, inflates the surplus shard count into the hundreds. Each shard is an independent Lucene instance, so it eats into node CPU, JVM (Java Virtual Machine) heap, and disk I/O. Cluster state updates and node restarts slow down as a result.
Continuously accumulating data such as time-series logs is handled with ILM (Index Lifecycle Management). Indexes are rolled over at a given size or age, and older indexes are moved through hot, warm, and cold tiers to lower cost. That keeps any one index's shards from growing without bound.
Rebalancing and failure behavior
Adding shards or losing a node causes the cluster to relocate shards. Rebalancing physically moves data between nodes, so it consumes a lot of network and disk. Excessive rebalancing during recovery can send performance sharply in the wrong direction.
Failure behavior follows directly from the design above. When a node dies and a primary disappears, a replica on another node is promoted to primary. An unstable master, on the other hand, destabilizes shard allocation itself and escalates into a cluster-wide operational problem.
Shard and replica design therefore has to account for recovery cost, not just steady-state performance. With no replicas, losing a primary means losing data. With too many replicas, write and storage costs rise and there is more data to move during recovery.
Summary
Elasticsearch's sharding architecture interlocks two decisions: splitting an index into shards and replicating those shards. The primary count is tied to the routing hash and fixed at creation, while the replica count is tuned at runtime to match read load. Separating node roles keeps the master's control load from mixing with the data nodes' I/O load. Keeping shard size in the 30-50GB range and avoiding shard explosion and excessive rebalancing is the baseline for running a stable cluster.