What Is Bulk Indexing Tuning in Elasticsearch?
A complete, plain-English guide to loading huge amounts of data into Elasticsearch quickly and safely: how bulk requests travel through the cluster, which settings really matter, what to measure, and which mistakes to avoid.
Imagine moving to a new house. You could carry one item at a time: a spoon, then a plate, then a sock, walking to the truck and back every single time. Or you could pack everything into boxes and carry a full box on each trip. Boxes are obviously faster. But there is a catch. If the boxes are too small, you still spend your day walking. If they are too big, nobody can lift them and the doorway gets blocked. If ten helpers run through the same door at once, they collide. And if the new house is not ready, everything piles up in the hallway.
Loading data into Elasticsearch works in much the same way. Sending documents one at a time is the spoon-by-spoon method. The Bulk API is the box. Bulk indexing tuning is the craft of choosing the right box size, the right number of helpers, and the right house settings, so that data flows in at full speed without breaking anything. This guide starts from zero, explains what happens inside the cluster, and ends with the numbers, the settings, and the traps that matter in production.
1What Bulk Indexing Tuning Actually Is
Three plain ideas, put together: indexing, bulk, and tuning. Once each is clear, the whole subject makes sense.
Indexing means adding a document to Elasticsearch so that it can be searched. A document might be a product, a customer record, or one line from a log file. When you index it, Elasticsearch does not just save it. It also breaks the text into words, builds lookup structures, and stores the values in several shapes so that searching later is fast. That extra work is why indexing costs more than simply writing a line to a file.
The Bulk API lets you send many indexing operations in one single request. Each operation in the request says what to do (add a new document, replace one, update one, or delete one) and, for adds and replaces, carries the document itself. The cluster processes the whole batch and answers with one response that lists what happened to every item.
Tuning means adjusting the knobs that control speed. Some knobs live in your own loading program, such as how many documents go in each request and how many requests run at once. Some live in the index settings, such as how often new data becomes searchable and how many copies are kept. Some live in the cluster itself, such as the number of shards and the kind of disks. Bulk indexing tuning is the work of setting all of these knobs sensibly, using measurements instead of guesses.
Picture a bakery that must fill a huge order of 100,000 rolls. The baker can bake one roll, sell it, and start the oven again, which wastes most of the day heating and cooling. Or the baker can load big trays into the oven, bake in large batches, and hand over full boxes. Tuning is choosing the tray size, the number of ovens, and the order in which things are done, so that the ovens are always busy but never overloaded.
The three places where tuning happens
The client
Your program or ingest tool: bulk size, number of parallel workers, compression, retries, and how failures are handled.
The index
Settings such as refresh interval, replicas, translog behavior, document ids, and the mapping that describes each field.
The cluster
Shard count, node roles, disks, memory, and network. These set the ceiling that no client trick can break.
Every speed-up in this guide costs something else: search freshness, safety of the data, memory, or simplicity. Good tuning is not “maximum speed.” It is the best speed you can get while keeping the promises your business needs, such as not losing data and not breaking live searches.
The one-sentence version
If you remember one line from this chapter, make it this: bulk indexing tuning is the practice of sending well-sized batches, from the right number of parallel workers, into an index whose settings favor writing, while watching the cluster’s health signals so that you go as fast as it can safely go and no faster.
2Why It Exists: The Cost of Doing Things One by One
Every tuning practice exists because something was painfully slow or dangerously fragile. Here are the pains.
Every request sent over a network carries a fixed overhead. The client opens or reuses a connection, sends a header, waits for the cluster to receive it, waits for the work, and waits for the answer. This waiting is the same whether the request carries one document or five thousand. When you index documents one at a time, you pay that overhead again and again, and most of your time goes to waiting instead of working.
A numbers walkthrough
Let us use some invented numbers to feel the difference. Suppose one round trip to the cluster costs 3 milliseconds of pure waiting, and indexing one document takes about 1 millisecond of real work. You need to load 1 million documents.
Sending them one by one, in a single line, costs about 4 milliseconds each. That is 4,000 seconds, or a bit over an hour. Now send them in bulk requests of 5,000 documents. That is 200 requests. Each one pays the 3 milliseconds of waiting once, plus the real work of indexing 5,000 documents, which is about 5 seconds if you keep the same 1 millisecond estimate. The total is roughly 1,000 seconds. Real clusters can index many documents in parallel, so with several workers you might finish in a couple of minutes. The exact figures will differ on your system, but the shape never changes: batching removes most of the waiting.
Mailing 1,000 letters by making 1,000 separate trips to the post office wastes your day on walking. Putting them all in one sack and making a single trip is faster, even though the sack is heavier. The walk is the fixed overhead. The letters are the useful work.
A short history
Early days: the Bulk API arrives
Very early in Elasticsearch’s life, the Bulk API was added so that clients could group many operations into one request. It has been the standard way to load data ever since.
Separate thread pools for writes
Elasticsearch handled bulk and single-document writes with separate worker pools with separate queues. In later releases they were merged into a single pool named “write.”
Rejections as a safety valve
When too much work arrived at once, nodes started to refuse new work with an “HTTP 429 Too Many Requests” answer. This let clusters protect themselves, but it also meant that clients had to learn to slow down and retry.
Memory-based indexing pressure
Around version 7.9, Elasticsearch began tracking how many bytes of indexing work were in flight, and started refusing new work when a memory limit was reached. This guards the node against running out of memory, even when the queue is not yet full.
Today: guard rails plus good practice
The cluster protects itself much better than before. Your job is to feed it well: reasonable batch sizes, sensible parallelism, and graceful handling of the “slow down” signal.
The exact release for each step is worth double-checking in the release notes of your version. The direction of travel is what matters: from “send anything and hope” toward “the cluster pushes back, and clients cooperate.”
Why not just send as fast as possible?
A natural idea is to turn every knob to the maximum: giant batches, hundreds of workers, no pauses. The result is usually worse, not better. Giant batches consume memory on the node that receives them. Too many workers fill the queues and trigger rejections. Rejections lead to retries, and retries add more load. The cluster ends up busy handling failures instead of indexing. The goal is a steady, well-fed pipeline, like a conveyor belt that is always full but never jammed.
3Core Concepts You Need First
A dozen words explain nearly everything in this guide. Learn them here, and later chapters will read like common sense.
Document and index
A document is one record. An index is a named collection of documents, like a table, that Elasticsearch searches together.
Shard
An index is cut into pieces called shards, and each piece can live on a different node. A primary shard receives new documents first.
Replica
A copy of a primary shard, kept on a different node for safety and for extra search capacity.
Segment
A small, sealed, read-only file set inside a shard. New data lands in new segments, and background merges combine them.
Bulk request
One HTTP call carrying many operations. Each line pair describes one operation. The answer lists the result of each.
Translog
A running diary on disk of recent operations, so that data can be recovered after a crash before it is fully saved in segments.
Refresh
The moment when recently indexed documents become visible to searches. By default it happens about once per second.
Flush and merge
Flush saves segments safely to disk and clears old translog. Merge combines small segments into larger ones in the background.
Thread pool and queue
Each node has a fixed group of worker threads for writes, and a waiting line for extra work. When the line is full, new work is refused.
Backpressure and 429
The cluster’s way of saying “slow down.” A 429 answer means the work was refused because the node was too busy.
Throughput
How many documents, or how many bytes, go in per second. This is the main number you are trying to raise.
Latency
How long one request takes from sending to answer. Higher batch sizes usually raise latency but also raise throughput.
The write path in one breath
When a document arrives, Elasticsearch analyzes it, meaning it turns text into searchable words. It places the result in an in-memory buffer and also records the operation in the translog on disk. At the next refresh, the buffer becomes a new small segment that searches can see. Later, a flush stores segments durably and trims the translog. Meanwhile, background merges tidy up the growing collection of small segments. Every tuning trick in this guide is really a way to make one of these steps cheaper, rarer, or more parallel.
Think of a busy kitchen. Orders arrive on a ticket rail (the queue). The cook prepares dishes on the counter (the in-memory buffer) while a waiter writes every order in a notebook (the translog) in case the power fails. Every so often a tray of finished dishes is carried out to the dining room (refresh). At closing time the notebook is filed away (flush). During quiet moments, helpers combine small leftovers into bigger containers (merge).
Backpressure is a feature, not a bug
Beginners often see the 429 answer as an error to be suppressed. It is actually a healthy signal, similar to a traffic light turning red. The cluster is telling the client, honestly, that it needs a short pause. A well-tuned loader listens, waits a little, and tries again, ideally with a growing delay each time. A badly tuned loader either ignores the signal and loses data, or hammers harder and makes the jam worse.
Two ways to measure speed
Documents per second is easy to explain, but it depends on how large the documents are. Megabytes per second is fairer when document sizes vary. When you compare two experiments, use the same documents in both, or use bytes per second, so that you are comparing the tuning and not the data.
An HTTP 200 answer on a bulk request does not mean that every document was accepted. The request as a whole can succeed while individual items fail. Always check the per-item results, which Chapter 13 explains in detail.
4Architecture and Components
Where does a bulk request go after it leaves your program, and which parts of the cluster can slow it down?
A production cluster commonly has a load balancer in front, some nodes that act as coordinators, and many data nodes that hold the shards. The bulk request enters at the front, is taken apart by a coordinating node, and its pieces are sent to the primary shards that own each document. The primaries then pass the work on to their replicas.
flowchart TB
Client["Indexing workers: your application or ingest tool"] --> LB["Load Balancer"]
LB --> Coord["Coordinating Node: splits the bulk by shard"]
Coord --> P1["Primary Shard 1"]
Coord --> P2["Primary Shard 2"]
subgraph NodeA["Data Node A"]
P1
R2["Replica of Shard 2"]
WQA["Write thread pool and queue"]
end
subgraph NodeB["Data Node B"]
P2
R1["Replica of Shard 1"]
WQB["Write thread pool and queue"]
end
P1 --> R1
P2 --> R2
P1 -.-> WQA
P2 -.-> WQB
Read the picture from top to bottom. Your workers send requests through the load balancer, which spreads them across coordinating nodes. The coordinating node looks at each document’s target shard, using its id or routing value, and groups the documents by shard. Each group is sent to the node holding that primary shard. On that node the work waits in the write queue for a free worker thread. The primary indexes the documents and then forwards the same operations to each replica, which indexes them again on its own node. Notice that the replica of Shard 1 sits on Node B, next to the primary of Shard 2, so every node is doing both primary work and replica work at once.
The main components
Load balancer
Spreads client connections across nodes so that no single node becomes the doorway for all traffic.
Coordinating node
Receives the bulk request, splits it by target shard, sends the pieces out, gathers the answers, and builds the final response.
Primary shard
Owns the documents of its slice. It indexes each operation first, and decides whether the operation succeeded.
Replica shard
Repeats the primary’s operations on its own copy of the data. The acknowledgment normally waits for the in-sync replicas.
Write thread pool
A fixed group of threads, about one per available processor, plus a waiting queue. Full queues cause 429 answers.
Indexing pressure
Counts the bytes of indexing work in flight at each stage and refuses new work above a limit, by default a share of the heap.
Ingest pipeline
Processing steps run on documents before indexing, such as parsing fields or adding data. Useful, but they cost extra CPU.
Disks and file system cache
Where translogs and segments are written. Fast disks and spare memory for the operating system make writes and merges much smoother.
Walking one bulk request through the machine
Your worker builds a batch
It collects a few thousand documents, describes each operation, and sends one request, often compressed to save network bandwidth.
The coordinating node checks its memory budget
The size of the request is added to the indexing pressure counter. If the limit would be exceeded, the whole request is refused with a 429.
The request is split by shard
Documents are grouped by the shard that owns them, and each group is sent to that shard’s primary.
The primary queues and indexes
The group waits for a write thread. If the queue is full, its items are refused with 429. Otherwise the documents are analyzed, buffered, and recorded in the translog.
Replicas repeat the work
The primary sends the operations to each in-sync replica, which indexes them independently and confirms.
The answer flows back
The coordinating node collects every shard’s results, assembles one response with a result for each item, and returns it to your worker.
Every step in this journey can be the slow one. Network limits show up at step 1, memory limits at step 2, uneven shard layout at step 3, CPU and disk at step 4, and replica lag at step 5. Tuning is largely the art of discovering which step is your bottleneck.
5Internal Working: Inside a Shard While Data Pours In
Now we open the shard itself. Most of the settings you will tune act on the small machinery shown below.
flowchart LR
B["Incoming documents"] --> IB["In-memory indexing buffer"]
B --> TL["Translog on disk"]
IB -->|refresh| SEG["New small segment: now searchable"]
SEG -->|merge| BIG["Larger merged segment"]
TL -->|flush| COM["Lucene commit: segments saved, translog trimmed"]
SEG --> COM
Analysis and structure building
Text fields are split into words, lowercased, and processed by analyzers. Each field is also turned into the structures that searches, sorting, and aggregations will need. The more fields and the more structures per field, the more CPU this step uses. It is often the biggest single cost.
The in-memory buffer
The results collect in memory inside the shard. Several threads can write into the same shard at once, each filling its own part of the buffer, which is why one shard can use several processor cores.
The translog append
At the same time, the operation is appended to the translog. By default the translog is synced to disk before each request is acknowledged, so that an acknowledged write survives a crash. That sync is a real cost on every request.
Refresh
Every second by default, the buffer is written out as a new small segment that searches can see. Each refresh creates new small files. Frequent refreshes mean many small segments and extra work.
Flush
Periodically, or when the translog grows past a size threshold, a flush makes all segments durable on disk and lets old translog files be discarded.
Merge
In the background, small segments are merged into bigger ones. Merging reads and writes a lot of data, so it competes with indexing for disk and CPU.
Throttling
If merges fall far behind, Elasticsearch deliberately slows down indexing threads so that the number of segments does not grow without limit. Throttling is a sign that disk or merge capacity is the bottleneck.
Imagine a library receiving thousands of new books. The books are first stacked on a receiving table (buffer) while a clerk writes each title in a logbook (translog). Every second, the clerk could roll a cart of books to the shelves so patrons can see them (refresh), but each trip takes effort. Now and then the staff reorganize many small carts of books into large tidy shelves (merge). If the reorganizing staff cannot keep up, the receiving desk is told to slow down (throttling).
Why frequent refreshes hurt bulk loads
Refresh is what gives Elasticsearch its “near real-time” feel. But every refresh writes a new segment, and every new segment adds work for the future: it must be searched, and it will eventually need to be merged. During a large load, nobody is waiting for the newest document to become searchable, yet the cluster keeps refreshing every second. Loosening the refresh, as Chapter 8 explains, saves a lot of work.
Why replicas double the work
In the standard design of Elasticsearch, a replica does not simply copy files from the primary. It receives the same operations and indexes them itself, doing the analysis and buffering again. With one replica, your cluster does the indexing work twice. That is the price of safety and search capacity, and it is why turning replicas off during a first-time load can nearly halve the total indexing effort. Later, when replicas are added back, they are created by copying finished segment files, which is cheaper than indexing every document a second time.
Why the merge scheduler matters
The number of segments a shard accumulates depends on how fast it is fed and how fast it can merge. Merges are driven by a policy that groups segments of similar size. Fast disks, such as solid-state drives, allow merges to keep pace. Slow or shared disks are the classic cause of indexing that starts fast and then falls off a cliff as merges pile up.
If your load runs fast for a few minutes and then suddenly slows to a crawl, suspect merges and disk. Check the throttling numbers in the indexing statistics before you touch anything else.
6Data Flow and Lifecycle of a Bulk Load
A big load is a small project with a beginning, a middle, and an end. Most disasters happen because someone skipped the end.
flowchart LR
S1["Plan: mapping, shards, sizing"] --> S2["Prepare: refresh off, replicas 0, translog relaxed"]
S2 --> S3["Load: parallel bulk workers with retries"]
S3 --> S4["Verify: counts and failed items"]
S4 --> S5["Restore: refresh on, replicas on, translog durable"]
S5 --> S6["Optional: force merge on read-only data"]
S6 --> S7["Serve searches"]
The phases in order
Plan
Decide the mapping, the number of shards, and the target index name. Create the index with the right settings before any data arrives, so that Elasticsearch never has to guess field types on the fly.
Prepare
For a one-time load into a new index, loosen the refresh, remove replicas, and consider a relaxed translog. These settings are dynamic, so they can be changed while the index is open.
Load
Start parallel workers sending well-sized bulk requests. Watch the rejection counts and slow down when the cluster pushes back. Retry only the failed items.
Verify
Compare the number of documents in the index with the number you meant to send. Review the list of failed items and decide what to do with each.
Restore
Put the refresh interval back, bring replicas back to their normal number, and return the translog to full durability. Call a refresh so that everything becomes searchable at once.
Optional force merge
If the index will not receive more writes, merging it down to fewer segments can make searches faster and storage tidier. Do this after the load and before heavy search traffic starts.
Serve
Let users search. Keep an eye on the cluster while replicas finish copying, because that copy also uses disk and network.
Think of a stage crew setting up a concert. They plan the layout, lock the doors while the equipment is carried in, and only when everything is ready do they turn on the lights and open the doors to the audience. If they open the doors halfway through the setup, the audience sees a mess and gets in the way of the crew.
One of the most common production incidents is an index left with refresh switched off and no replicas, weeks after the load finished. Searches then never see new data, and a single node failure destroys the only copy. Put the restore step in your runbook, in your script, and in your review checklist.
Three kinds of loads
Not all bulk indexing is the same, and the right tuning depends on the kind.
- Initial load: a one-time fill of a new index from another system. Speed is the top goal. Aggressive settings are safe because you can always start again.
- Continuous ingestion: a steady stream such as logs or events. You cannot switch off refresh or replicas, because searches and safety are needed all the time. Tuning here is about steady, efficient batches and headroom for spikes.
- Reindexing: copying data from one index to another, usually after a mapping change. It behaves like an initial load, with the extra detail that the source index is being read at the same time.
What happens to searches during the load
With refresh loosened or switched off, new documents will not appear in search results until the next refresh. That is fine for a fresh index nobody queries yet. For an index that live users search, do not switch refresh off. Choose a longer interval, such as ten or thirty seconds, so that data still becomes visible in a reasonable time while the refresh cost drops sharply.
7Tuning the Client Side
The cheapest tuning happens in the program that sends the data. No cluster change is needed, and the gains are often large.
| Knob | Sensible starting point | What to watch |
|---|---|---|
| Bulk request size | About 5 to 15 megabytes, then adjust slowly | Throughput stops rising, or rejections and memory use climb. |
| Parallel workers | A handful per target node, raised step by step | CPU saturation, growing queues, 429 answers. |
| Compression | On, if the network is a limit and CPU is spare | Client CPU usage and network throughput. |
| Response trimming | Ask for only the fields you need in the answer | Response size and parsing time on the client. |
| Retries | Only for failed items, with a growing delay | Retry storms that add load during trouble. |
| Connection handling | Reuse connections, spread them across nodes | One node handling far more requests than the rest. |
| Timeouts | Generous enough for a full batch to finish | Timeouts that cause duplicate sends. |
Choosing the bulk size
The right size is measured in bytes, not in document count, because documents vary in size. Elasticsearch’s own tuning guidance suggests starting with requests of roughly 5 to 15 megabytes and increasing slowly. For documents around one kilobyte, that means several thousand documents per request. Beyond a certain point, larger requests stop helping and start hurting: they consume memory on the coordinating node, they take longer to retry when they fail, and they can trigger rejections from the memory guard. There is also a hard ceiling on request size set by the HTTP settings of the cluster, which by default is 100 megabytes.
A delivery van that carries only one parcel per trip wastes fuel. A van that is overloaded slows down, risks breaking down, and if it does break down, everything inside is late. A good driver loads the van comfortably full, not empty and not bursting.
Finding the sweet spot, step by step
Use one worker first
Send batches from a single worker and record documents per second at several sizes, such as 2, 5, 10, and 20 megabytes.
Pick the plateau
Choose the smallest size where throughput stops improving. Smaller batches are gentler and easier to retry.
Add workers gradually
Raise the number of parallel workers one at a time. Throughput should climb, then flatten.
Back off from the edge
Stop a little before the point where rejections start. A pipeline running at the very edge fails at the first spike.
Parallel workers
A single sender can rarely fill a cluster, because it spends part of its time preparing the next batch or waiting for the answer. Several workers overlap those pauses. How many workers is best depends on how many data nodes, cores, and shards you have. The rule is simple: add workers until the cluster starts pushing back or the CPU on the data nodes is fully busy, then use a comfortable amount less than that.
Compression and response trimming
Bulk bodies are text, and text compresses very well. If the network is a bottleneck, and clients typically support compressing the request, turning it on can reduce transfer time at the price of a little CPU on both ends. The response can also be trimmed: by default it lists every item in detail, which is wasteful when nearly all items succeed. Requests can ask the cluster to return only the parts of the answer that you need, for instance the error flag and the details of failed items, which cuts down both network traffic and client parsing.
Retries done right
When a batch comes back with a “too many requests” signal, do not resend it immediately. Wait, and let the wait grow if the problem persists. Adding a little randomness to the wait prevents all workers from retrying at exactly the same moment. When only some items in a batch failed, retry only those items, not the whole batch, so that you do not repeat work that already succeeded.
The official Elasticsearch client libraries include bulk helpers that handle batching by size, parallelism, retries, and per-item error reporting. Ingest tools such as Logstash and Beats also have their own batch and worker settings. The same principles in this chapter apply to them: measure the size in bytes, raise parallelism slowly, and respect rejections.
Spreading the load
If every request goes to one node, that node’s coordinating work and memory become the bottleneck while the others sit idle. Point clients at several nodes, or at a load balancer that spreads requests evenly. For large clusters, dedicated coordinating or ingest nodes can take the splitting work off the data nodes.
8Tuning the Index Settings
A handful of index settings decide how much background work every document creates. Loosening them for a load can change the speed dramatically.
| Setting | Default | During a one-time load | After the load |
|---|---|---|---|
index.refresh_interval | 1 second | Switch off with -1, or use 30 seconds or more | Return to a value your searches can live with |
index.number_of_replicas | 1 | 0 for a first-time load that can be rerun | Return to 1 or more |
index.translog.durability | Sync on every request | Optionally asynchronous, only if you can rerun the load | Return to the request setting |
index.translog.flush_threshold_size | 512 megabytes | Optionally larger, so flushes happen less often | Return to default or keep if tested |
indices.memory.index_buffer_size | 10 percent of heap | Node setting, usually left alone | No change |
| Document ids | Generated by Elasticsearch | Prefer generated ids where possible | No change |
Most of these are dynamic index settings, so they can be changed on a running index without a restart. The memory setting is a node-level setting and needs a restart to change, so it is usually planned in advance if changed at all.
Refresh interval
Refresh makes new data searchable and creates a new small segment each time it runs. Switching it off, or stretching it to a long interval, removes a large amount of repeated work. There is one detail worth knowing: modern Elasticsearch skips scheduled refreshes on shards that nobody has searched for a while, by default around thirty seconds. That means an index that no one is querying may already refresh less often than you expect. Setting the value explicitly is still wise because it makes the behavior predictable.
A shop that updates its window display every single second would spend all day rearranging and never selling anything. Updating the window once at the end of a big delivery is much more sensible.
Replicas
Because each replica indexes documents itself, zero replicas means your cluster does the analysis and indexing work only once. For a first-time load this can nearly double the speed. The trade-off is safety: while there are no replicas, a lost node means lost data, and the load must be redone. That is acceptable only when the source data still exists and the load can be repeated. When replicas are restored, the new copies are built from finished segment files, which is faster than replaying every document. Restoring them still uses disk and network, so plan for that time before opening the index to heavy traffic.
The translog
By default, Elasticsearch syncs the translog to disk before answering each request, so a confirmed write survives a crash. Switching to asynchronous durability syncs every few seconds instead, which saves time on each request but means that a crash can lose the last few seconds of acknowledged writes. For a load that can be rerun, that is a reasonable gamble. For continuous ingestion of data you cannot recreate, keep the safe default.
Only relax durability or replicas when you can answer “yes” to this question: if the whole load vanished right now, could I run it again from the source? If the answer is “no” or “not sure,” keep the safe settings.
The indexing buffer
The in-memory buffer holds recently indexed documents before they are written out as a segment. Its total size is shared among all shards that are actively indexing on the node, and it defaults to ten percent of the heap. A larger buffer means bigger, fewer segments, which is good, but the benefit shrinks as the buffer grows, and the memory comes out of the same heap that everything else needs. Most teams leave it alone, and only consider changes when a node has many busy shards.
Auto-generated ids
When you give a document your own id, Elasticsearch must first check whether a document with that id already exists in the shard, because the new operation might be a replacement. That check is real work. When Elasticsearch generates the id itself, it can skip the check, because it knows that a fresh id has never been used. For append-only data such as logs and events, generated ids make indexing noticeably faster.
The trade-off is repeatability. With your own ids, sending the same document twice simply overwrites it, which makes retries safe. With generated ids, a retry after an uncertain timeout can create a duplicate. Choose according to your data: append-only streams favor generated ids, and data that must be updated or deduplicated needs your own ids.
Things that slow indexing on purpose
- Index sorting orders documents inside segments to help certain searches, but adds cost at indexing time. Use it only if you truly need it.
- Stronger compression of stored data saves disk space but costs CPU while writing. The default is faster to write.
- Waiting for extra shard copies before acknowledging adds delay. Keep the defaults unless you have a reason.
- Refreshing on every request by asking for it in the bulk call defeats the whole point of batching.
9Mappings and Documents: Making Each Document Cheaper
The cost of indexing one document is decided long before the bulk request is sent. It is decided by the shape of the document and the mapping that describes it.
A mapping is the blueprint that tells Elasticsearch what type each field has and how it should be stored and searched. Every field you index has a price: analysis, index structures, storage for sorting and grouping, and space for the original text. The more fields and the more structures per field, the more work per document. Cutting unnecessary work at this level speeds up every single request.
Index only what you search
A field that is never searched, sorted, or grouped can be marked as not indexed. It is still kept in the original document if needed.
Skip what you do not use
Fields that will never be sorted or aggregated do not need the column-style storage. Text fields that will not be scored by length can drop that extra data.
Keyword versus text
Exact values like codes, ids, and statuses belong in keyword fields, which skip the heavy text analysis.
Explicit mappings
Define the mapping before loading. Letting Elasticsearch guess types for every new field costs time and can guess wrongly.
Stop field explosions
Documents with thousands of different field names bloat the cluster. Limit dynamic fields, or store such maps in a special field type built for them.
Be careful with nested objects
Each nested object is stored as a hidden document of its own, so a document with many nested items costs many times more to index.
Filling out a form with fifty boxes takes much longer than a form with ten boxes. If a box is never read by anyone, removing it saves time on every single form, and there will be thousands of forms.
Dynamic mapping: convenient but costly
When a document contains a field that the index has never seen, Elasticsearch normally creates a mapping for it automatically. This is called dynamic mapping. It is convenient for exploring but has two costs during bulk loads. First, each new field requires an update to the cluster’s shared state, which is slow when it happens often. Second, the automatic guess may be wrong or wasteful, such as indexing every string as both text and keyword. For production loads, define the mapping first, using an index template, and set dynamic behavior to strict or to off for unknown fields if your data is well understood.
Document size and shape
Very large documents are slow to analyze and take large amounts of memory in each stage of the write path. If a document contains a huge blob of text or many repeated sections, consider whether all of it must be in Elasticsearch, or whether a reference to the full content stored elsewhere would do. Documents under a few kilobytes index most smoothly.
Work before the bulk request
Cleaning, joining, and enriching data can happen in two places: in your loading program before sending, or in an ingest pipeline inside Elasticsearch. Ingest pipelines are handy, and can be attached to a request, but they spend the cluster’s CPU on work that your own program could do on cheaper machines. If the cluster is your bottleneck, moving heavy transformations upstream is one of the most effective changes you can make.
An index template applies a mapping and settings automatically to every new index that matches a name pattern. For time-based data, where a new index appears every day or on every rollover, templates guarantee that each new index starts with the right blueprint and never falls back on guesswork.
10Hardware, Shards, and Cluster Layout
Settings can only take you as far as the machinery allows. This chapter covers the ceiling.
Shards: the unit of parallel work
Documents are spread across the primary shards of an index. Each shard can accept several concurrent writers, but the number of shards also decides how the work spreads across nodes. An index with a single primary shard sends all its indexing to one node, no matter how big the cluster is. An index with primaries spread over all data nodes lets every node do its share.
Too many shards cause different trouble. Each shard has fixed overhead in memory, file handles, and cluster state, and a large number of tiny shards makes both indexing and searching less efficient. A common guideline is to keep shards in the range of tens of gigabytes for search-oriented data, and to avoid a huge number of shards per gigabyte of heap. For time-series data, rolling over to a new index when the current one reaches a target size keeps shards sensible without manual planning.
If a supermarket has one checkout lane, it does not matter how many cashiers are on the payroll; only one customer is served at a time. Ten lanes help a lot. But a hundred tiny lanes, each with its own equipment and staff, waste money and floor space.
Hardware that helps indexing
Solid-state drives
Indexing and merging create heavy, mixed disk traffic. Fast local SSDs are the most common upgrade that fixes the “starts fast, then crawls” pattern. Avoid slow network file systems.
Enough cores
Analysis is CPU-heavy, and the write thread pool is sized to the number of processors. More cores mean more documents processed at once.
Moderate heap, generous file cache
Give the heap no more than about half of the machine’s memory, and stay below the size at which Java loses compact pointers, roughly 30 gigabytes. The rest is used by the operating system to speed up disk work.
Bandwidth and low latency
Replication and shard recovery copy a lot of data between nodes. A slow or busy network is a hidden bottleneck for loads with replicas.
No swapping
If the operating system moves parts of the Elasticsearch process to disk, everything becomes slow and unpredictable. Turn swapping off or lock the memory.
Dedicated node roles
Separate coordinating, ingest, and data roles on larger clusters, so that heavy transformations and request splitting do not compete with shard work.
Hot data on fast nodes
In time-based systems, the newest data is written to and searched the most, and the oldest data is mostly just stored. A layered design places the newest indices on the fastest nodes, the hot tier, and moves older indices to cheaper machines automatically. Indexing then always lands on hardware chosen for it, while cheaper hardware holds the archive.
Do the shard arithmetic before the load
Estimate the total size of the data, decide the target shard size, and divide. Then check that the result spreads evenly across your data nodes. If you have six data nodes and choose five primary shards, one node will do no primary work. Choosing six, or a multiple of six, gives every node an equal share. The number of primary shards cannot be changed on an existing index without a rebuild, so it pays to decide carefully.
A good habit is to benchmark on one node with one shard first, to learn how many documents per second a single shard can absorb. Then multiply by the number of shards you plan to use and compare with your target. If the numbers do not add up, you learn it before the big load, not during it.
11Performance and Scalability: Finding the Bottleneck
A pipeline is only as fast as its slowest stage. Tuning the wrong stage changes nothing. The skill is knowing where to look.
Bulk indexing passes through many stages: your program, the network, the coordinating node, the write queue, the CPU that analyzes documents, the memory buffer, the translog, the disk, the merges, and the replicas. At any moment, one of these is the limit. Speeding up any other stage will not raise the total. The flowchart below is a simple way to find the limiting stage.
flowchart TD
A["Throughput is too low"] --> B{"Are there 429 rejections?"}
B -- Yes --> C["Reduce workers or bulk size and check queues and indexing pressure"]
B -- No --> D{"Is data node CPU near full?"}
D -- Yes --> E["Cut analysis cost: fewer fields, move pipelines upstream, add cores or nodes"]
D -- No --> F{"Is the disk busy or indexing throttled?"}
F -- Yes --> G["Improve disks, loosen refresh, check merges, add shards"]
F -- No --> H{"Is the client the limit?"}
H -- Yes --> I["Add workers, adjust batch size, compress, spread across nodes"]
H -- No --> J["Check network, replicas, and uneven shard layout"]
Think of a factory line with five stations. If the painting station is slow, hiring more people at the packing station does nothing. You look for the station with a pile of unfinished work in front of it. In an indexing pipeline, that pile is the queue, the rejections, the high CPU, or the throttling counter.
Where the biggest wins usually are
| Change | Typical effect | Effort | Main risk |
|---|---|---|---|
| Use bulk requests of sensible size instead of single writes | Very large | Low | Batches too big for memory |
| Add parallel workers | Large | Low | Rejections if overdone |
| Loosen or switch off refresh during load | Large | Low | Forgetting to restore |
| Remove replicas for first-time load | Up to about double | Low | No safety copy during load |
| Trim the mapping | Medium to large | Medium | Losing features you needed |
| Auto-generated ids | Medium | Low to medium | Duplicates on retry |
| Faster disks and more cores | Large when hardware is the limit | High | Cost |
| More shards and nodes | Scales with count | High | Over-sharding overhead |
Read the table from top to bottom. The easiest changes are also the most powerful, so try them first. Only when they are exhausted does it make sense to change the mapping, and only after that to pay for hardware.
A worked example
Imagine a company must load 100 million product records, each about one kilobyte, into a new index within two hours. That is about 100 gigabytes of source data. Two hours is 7,200 seconds, so the target rate is roughly 14,000 documents per second, or about 14 megabytes per second.
A quick test on one node with one shard shows that a single shard absorbs about 5,000 documents per second with the default settings, and about 9,000 with refresh switched off and no replicas. To reach 14,000 per second with a safety margin, the team wants at least three shards working at once. They choose six primary shards on six data nodes, so that every node carries one primary and load is even. They start with 8-megabyte batches, and add workers until rejections just begin, then back off one step. The invented numbers are only an illustration, but the method is real: measure one shard, multiply, add margin, and verify.
Scaling out
Indexing scales nearly linearly with the number of shards and nodes, as long as the load is spread evenly and clients send to all of them. Uneven spread is the usual reason scale-out disappoints: one hot shard, or one node receiving all client traffic, becomes the limit again. Watch the per-node numbers, not only the cluster totals.
The cost of the first minutes
Many loads look fast at the start and slow later. At the start the buffers are empty, segments are few, and the file cache is cold but idle. As data grows, merges become bigger, disks become busier, and throttling may begin. Judge a tuning change by sustained throughput over a long enough run, not by the first thirty seconds.
12Advantages, Disadvantages, and Trade-offs
Every speed-up is a trade. Here is the honest balance sheet.
Advantages of tuning
- Loads that took days can finish in hours.
- Lower cost: the same job runs on fewer or smaller machines.
- Fewer rejections and retries, so a steadier pipeline.
- Less pressure on the cluster, leaving room for live searches.
- A repeatable runbook for migrations and reindexing.
- Better understanding of the cluster’s real limits.
Disadvantages and risks
- Temporary settings can be forgotten and cause outages later.
- Relaxed durability risks losing recent writes on a crash.
- No replicas means no safety copy during the load.
- More parallelism means more complexity and harder debugging.
- Over-tuning for one workload can hurt another.
- Benchmarks can mislead if the test data is unrealistic.
| Trade-off | Leaning toward speed | Leaning toward safety |
|---|---|---|
| Refresh interval | Long or off: fewer segments, data invisible for a while | Short: data visible quickly, more work |
| Replicas | Zero during load: fastest, but one failure loses data | One or more: safe, but every document is indexed twice |
| Translog durability | Asynchronous: fast, may lose the last seconds | Per request: safe, slower |
| Bulk size | Large: fewer round trips, heavier memory and retries | Moderate: gentler, easier to retry |
| Document ids | Generated: fastest, duplicates possible on retry | Your own: retry-safe, extra check per document |
| Mapping richness | Lean: fast to index, fewer search features | Rich: flexible searching, more indexing cost |
| Parallelism | High: more throughput, closer to rejection | Moderate: steady, with headroom for spikes |
Driving faster gets you there sooner, but you use more fuel, and you have less time to react to problems. A sensible driver goes as fast as the road, the weather, and the load allow, and slows down for corners. Tuning is choosing a safe speed for the road you have.
Which tuning fits which situation?
| Situation | Recommended stance |
|---|---|
| First-time migration, source data still exists | Aggressive: refresh off, no replicas, generated ids, restore at the end |
| Live index that users search | Moderate: longer refresh interval, keep replicas, tune batches and workers |
| Continuous log or event stream | Steady: safe durability, generated ids, headroom for bursts, alerts on rejections |
| Reindexing after a mapping change | Aggressive on the new index, with careful reading load on the old one |
| Data that cannot be recreated | Conservative: keep durability and replicas, accept slower loads |
13High Availability and Reliability: When Things Go Wrong
A fast load that silently drops data is worse than a slow one. Reliability is the other half of tuning.
Partial failure: the most important idea
A bulk request is not all-or-nothing. It is a bundle of separate operations, and each one can succeed or fail on its own. The response includes a flag saying whether any item failed and then a result for each item. If your program looks only at the overall HTTP status, it will miss failures, and documents will be quietly lost. Every serious loader reads the flag, walks through the items, and handles each failure by its type.
A courier delivers a crate of fifty parcels. The delivery note says “delivered,” but three parcels were left at the wrong address. Only by checking each parcel against the list do you find out. The overall word “delivered” is not enough.
Sorting failures into kinds
Too many requests (429)
The cluster was busy. Wait with a growing delay and resend those items. The data is fine.
Server or shard trouble (5xx)
A shard may be moving, or a node may be restarting. A retry after a pause often works. Escalate if it keeps failing.
Client errors (400 family)
A field has the wrong type, or the document is malformed. Retrying will never help. Send these to a separate place for inspection.
Version conflicts (409)
Happens when an operation refuses to overwrite an existing document. Treat as success or as a problem depending on your intent.
A place for the failures: the dead-letter store
Documents that can never be indexed, such as those with wrong types, should not block the pipeline or vanish. A common design writes them to a separate store, often called a dead-letter queue, along with the error message. Someone then repairs the data or the mapping and resends them. Without this, a single bad record can stall a whole load or, worse, disappear without a trace.
Timeouts and the unknown
When a request times out, you do not know whether it succeeded. The cluster may still be working on it, or it may have finished after your program stopped listening. This is why retry safety matters. With your own document ids, resending is harmless because the same document overwrites itself. With generated ids, a resent batch that had in fact succeeded creates duplicates. If duplicates are unacceptable, either use your own ids, or deduplicate later, or use generous timeouts and careful checks.
When a node fails during a load
If a node holding a primary shard goes down, one of its replicas is promoted to be the new primary, and indexing continues. With no replicas, that shard’s data is gone, and the shard is unavailable until the node returns. Because a first-time load with zero replicas has no safety net, it should be designed to be restartable: keep track of how far the load got, so that it can resume or replay a range without starting from zero.
Track progress
Record the last successfully indexed position from the source, such as an id range or file offset.
Make batches repeatable
Design each batch so that running it twice gives the same final state, usually by using stable ids.
Verify at the end
Compare counts between the source and the index, and inspect the dead-letter store.
Keep the source until verified
Do not delete the original data until the new index is confirmed complete and has its replicas.
Cluster health colors
During loads, the cluster health may show yellow when replicas are missing or still being built, and red if a primary shard is unavailable. Yellow during a zero-replica load is expected. Red is never acceptable and should stop the load until it is understood.
14Security Considerations
A loading job is a powerful client: it writes a lot of data, quickly, with credentials. It deserves careful handling.
Least privilege for loaders
Give the loading program only the permissions it needs, and only on the indices it writes. In Elasticsearch’s security model, permission to add documents is granted per index or index pattern. A loader that only creates new documents does not need rights to delete indices or read other data. Using API keys created for a single job, with a limited lifetime, is safer than sharing a general-purpose administrator account. If the key leaks, the damage is limited to what that job could do.
A delivery driver is given a key to the loading dock, not a master key to the whole building. If the key is lost, only the dock needs to be secured.
Protect the connection
Bulk requests carry your data in text form. Always use encrypted connections between your loader and the cluster, and between nodes. Keep the cluster on a private network and expose only what the loaders and users need. Rate limiting and authentication at the load balancer add another layer.
Guard against accidental or hostile overload
- Request size limits: the cluster caps the size of an HTTP request. Keep this cap in place, so that one huge request cannot exhaust memory.
- Indexing pressure: the memory guard refuses work beyond its limit, which protects nodes from both mistakes and abuse.
- Field limits: a limit on the number of fields per index stops hostile or careless documents from creating thousands of new fields.
- Separate workloads: keep untrusted or experimental loads on their own indices, or even their own cluster, so that they cannot starve production.
Sensitive data in failures and logs
When a document fails, the error message and the failed document often end up in logs or a dead-letter store. If the data is personal or confidential, those copies become a leak risk. Restrict access to those stores, encrypt them, set retention limits, and avoid printing full document bodies in ordinary logs.
Ingest pipelines and scripts
Pipelines can run scripts and call other steps on every document. Restrict who can create or change them, because a bad pipeline changes every document that passes through, and a slow one becomes a denial-of-service against your own cluster.
Turn on authentication and encryption, even for internal clusters. Many data leaks come from clusters that were “only internal” and had no protection at all.
15Monitoring, Logging, and Metrics
You cannot tune what you cannot see. These are the signals to watch, and how to read them.
Elasticsearch exposes statistics for nodes and indices through its statistics endpoints, and the compact table views show several of them at a glance. Monitoring tools, including Elastic’s own, chart these over time. Keep charts of these numbers next to your throughput chart, so that a change in speed can be matched with a change in cause.
| Metric | What it tells you | Warning sign |
|---|---|---|
| Indexing rate (documents per second) | Your actual throughput, calculated from the change in the total count over time | Sudden drops during a steady load |
| Indexing time and current indexing operations | How long indexing takes and how many operations are in flight | Time per document rising steadily |
| Write queue size | Work waiting for a free worker thread | Queue near its limit for long periods |
| Write rejections | Work refused because of a full queue | Any sustained increase |
| Indexing pressure memory and rejections | Bytes of indexing work in flight, and refusals by the memory guard | Rejections at coordinating or primary stage |
| Throttle time | Time indexing was deliberately slowed because merges lag | Any nonzero growth |
| Merge activity | Current merges and time spent merging | Merges constantly running at maximum |
| Segment count | How many segments each shard holds | Rapid growth in number |
| Refresh count and time | How often, and how expensive, refreshes are | Frequent refreshes during a load |
| JVM heap and garbage collection | Memory health of each node | Heap constantly high, long collection pauses |
| CPU, disk, and network on each node | Which hardware resource is the limit | One resource pinned at maximum |
| Failed item count from client | Data that did not get in | Any unexplained failures |
Reading the patterns
High rate, empty queues
Throughput is steady, rejections are absent, CPU is busy but not pinned. Leave it alone.
Queues full, many 429s
The cluster is being fed faster than it can work. Reduce workers or batch size, or add capacity.
Throttling and busy disks
Merges cannot keep up. Improve disks, loosen refresh, or slow the feed.
CPU pinned, disks calm
Analysis is the cost. Trim the mapping, move transformations upstream, or add cores.
Cluster idle, client busy
The loader cannot feed fast enough. Add workers, compress, or improve the source read speed.
One node hot, others idle
Shards or client traffic are unevenly spread. Fix layout or connection distribution.
Logging that helps
Elasticsearch can record slow indexing operations in a special log, with thresholds you choose, which helps you find the documents or fields that cost the most. On the client side, log the size of each batch, the time it took, the number of failed items, and the reasons for failure. When a load misbehaves, those logs are the first place to look.
How to benchmark honestly
Use realistic documents
Take a sample of real data. Synthetic documents that are smaller, simpler, or more repetitive than the real ones give flattering and useless results.
Start with one node and one shard
Find the rate that a single shard can absorb. Then scale by shards and nodes and compare with your target.
Change one thing at a time
If you change batch size and refresh interval together, you will not know which helped.
Run long enough
Merges and throttling appear after minutes, not seconds. A test that stops early misses the cliff.
Use proper tools and record results
Elastic’s benchmarking tool, Rally, can replay realistic workloads and produce comparable reports. Whatever tool you choose, write down the settings and the results of each run.
Alert on sustained write rejections, on indexing throttling, on rising failed-item counts, on heap staying near its limit, and on an index that still has refresh off or zero replicas long after a load ended.
16Design Patterns and Anti-patterns
Good habits make loads smooth and repeatable. Bad habits are the source of most late-night incidents.
Patterns that work
Load into a fresh index, then switch
Write the new data to a brand-new index with loading settings, verify it, restore normal settings, and then point an alias from the old index to the new one. Users never see a half-loaded index.
Aliases for every index
Applications read and write through aliases, not concrete index names. Swapping the alias makes migrations invisible and reversible.
Bounded worker pool with backoff
A fixed number of workers, each sending batches, each backing off with a growing, randomized delay when the cluster says “slow down.”
Dead-letter store for bad records
Records that cannot be indexed are saved with their error, so the pipeline keeps flowing and nothing is lost silently.
Rollover for time-based data
Write to a current index that automatically rolls over to a new one at a target size or age, keeping shards well sized and old data easy to manage.
Template-driven mappings
Index templates guarantee that every new index starts with the right mapping and settings.
A buffer in front of the cluster
For bursty sources, a message queue between producers and the loader smooths spikes, so that the cluster sees a steady flow it can handle.
A theatre does not let the audience in while the stage is being built. It builds behind the curtain, checks everything, and only then opens the curtain. The alias swap is the curtain.
Anti-patterns to avoid
What people do
They index documents one at a time, or in tiny batches of a few documents, from a loop in their program.
Why it hurts
Each request pays the full round-trip overhead, and each one forces its own translog sync. Throughput is a small fraction of what the cluster could do.
Better approach
Group documents into bulk requests of several megabytes, and send them from several workers in parallel.
What people do
They ignore 429 answers, or retry them immediately in a tight loop, and keep raising the number of workers to “push through.”
Why it hurts
More pressure produces more rejections, which produce more retries. The cluster spends its energy on refusing and on repeated work, and real throughput falls.
Better approach
Treat a rejection as a signal. Back off with a growing, randomized delay, retry only the rejected items, and reduce concurrency if rejections persist.
What people do
They switch off refresh and replicas for a load and never switch them back.
Why it hurts
New data never becomes searchable, or becomes searchable only at odd moments, and there is no safety copy if a node fails.
Better approach
Script the restore step as part of the load itself, and add an alert that looks for indices still in “loading mode.”
What people do
They trust the overall HTTP status of a bulk request and never check the individual items.
Why it hurts
Some documents fail quietly, for example because of a wrong field type, and the loss is discovered days later.
Better approach
Read the error flag on every response, inspect failed items, route them to a dead-letter store, and compare counts at the end of the load.
What people do
They ask for a refresh on every bulk request so that “documents are searchable right away.”
Why it hurts
Each request now creates a new segment and triggers extra work, cancelling much of the benefit of batching.
Better approach
Leave refresh to the interval setting. If some step truly must see the data, refresh once at the end of the load.
17Best Practices and Common Mistakes
A short list you could hand to a new teammate on their first day.
Best practices
Size batches by bytes
Start around 5 to 15 megabytes and adjust slowly using measured throughput.
Add workers gradually
Raise concurrency until rejections just begin, then step back for headroom.
Loosen for loads, restore after
Refresh and replicas are the two big levers. Write the restore step before you write the load.
Define it first
Use templates with explicit mappings. Index only what you search.
Generated ids for append-only data
Use your own ids only when you need updates or safe retries.
Check every item
Handle partial failures, retry the right ones, and keep a dead-letter store.
Spread evenly
Choose a shard count that divides work across all data nodes, and send requests to many nodes.
Change one thing at a time
Benchmark with real data, over long enough runs, and record every result.
Common mistakes
- Counting documents instead of bytes. A batch of 10,000 tiny documents and a batch of 10,000 huge ones are very different loads.
- Benchmarking with fake data. Synthetic documents that are too simple make every setting look better than it will be.
- Changing many things at once. When speed changes, you cannot tell which change did it.
- Sending everything to one node. The coordinating work and memory use of that node become the limit.
- Giving the heap too much memory. A huge heap starves the file cache and lengthens garbage-collection pauses.
- Letting dynamic mapping run wild. A stray field with thousands of names can slow the whole cluster.
- Using updates when inserts would do. A partial update still reads the old document and writes a whole new one, so it is more expensive than adding a fresh document.
- Forgetting that force merging is for finished data. Merging an index that still takes writes creates huge segments that are costly to keep tidy.
Myths versus facts
| Myth | Fact |
|---|---|
| The bigger the bulk request, the faster the load. | Gains flatten and then reverse: large requests use more memory and cost more to retry. |
| More threads always means more speed. | Past a point, more threads only add queueing and rejections. |
| A 200 status on a bulk request means everything worked. | Items can fail individually inside a successful response. |
| Turning off replicas is always safe for a load. | It is safe only if the load can be repeated from a source that still exists. |
| Refresh off means data is lost. | Data is stored and safe. It is just not searchable until the next refresh. |
| Tuning is a one-time task. | Data, mappings, and hardware change, so tuning should be revisited with measurements. |
| Partial updates are cheap. | The whole document is rewritten internally. |
Start with the easy, safe wins: proper bulk batches, a few parallel workers, and a longer refresh interval. Only take risks such as removing replicas or relaxing durability when the load can be rerun, and always restore the normal settings afterward.
18Real-World and Industry Examples
Where does bulk indexing tuning matter most?
Many well-known organizations have said publicly that Elasticsearch is part of their systems, including Wikipedia’s search, Netflix, Uber, and Stack Overflow. They rarely publish their exact loading settings, so the scenarios below are typical patterns for such workloads, not descriptions of anyone’s private configuration.
Large retailer: catalog migration
A retailer of the size of Amazon or eBay may need to load a whole product catalog into a rebuilt index after changing how products are described. Speed matters because the old and new systems run side by side. The team loads into a fresh index with refresh off and no replicas, verifies the count, restores settings, and swaps an alias. Users never see the half-finished index.
Log ingestion from thousands of servers
A streaming or ride-hailing service produces enormous volumes of log lines. Shippers on each server batch events and send them through a queue to a loader, which writes them in bulk to the current daily or rolled-over index. Here refresh cannot be off, and durability must stay safe, so tuning centers on steady batches, generated ids, headroom for spikes, and alerts for rejections.
Nightly database export
A business copies changed rows from a relational database into Elasticsearch every night. Rows are updated, not just added, so the team uses stable ids and accepts the extra check per document in return for safe retries. The batch job records how far it got, so that a failure at 3 a.m. resumes rather than restarts.
Metrics from connected devices
Thousands of devices send small readings every few seconds. A gateway groups them into larger batches before they reach the cluster, because tiny requests would waste the cluster on overhead. Data is append-only, so generated ids and time-based rollover keep everything simple and fast.
Reindexing after a mapping change
A fixed field type means a new index and a copy of all documents. The built-in reindex feature reads the old index in batches, by default of a thousand documents, and writes to the new one in bulk. Teams often raise the batch size and split the work into parallel slices, while limiting the read rate so that live searches on the old index are not harmed.
Onboarding a new customer
A multi-tenant software product imports a customer’s historical data the day they sign up. The import runs on a modest number of workers with a throttle, so that one big customer’s import does not slow everyone else’s searches.
In every example, the winning ideas are the same: batch by size, parallelize gently, listen to the cluster, loosen settings only when safe, restore them afterward, and check every result.
19Frequently Asked Questions
Short answers to the questions that come up most often.
20Summary and Key Takeaways
If you skim only one part of this guide, make it this one.
Bulk indexing tuning is the discipline of getting large amounts of data into Elasticsearch quickly, safely, and repeatably. It starts with batching, continues with careful settings and a lean mapping, rests on well-laid-out shards and good hardware, and is checked at every step by measurement. The cluster will tell you when you are going too fast. Your job is to listen.
Key Takeaways
- Batch by bytes: use bulk requests of roughly 5 to 15 megabytes to start, send them from several workers, and adjust using measured throughput.
- Listen to backpressure: a 429 answer means “slow down.” Back off with a growing delay and retry only the failed items.
- Loosen, then restore: for one-time loads, relax refresh and replicas, and always put the normal settings back and verify afterward.
- Make each document cheaper: define the mapping first, index only what you search, prefer generated ids for append-only data, and move heavy transformations upstream.
- Check every result: a successful response can hide failed items, so read per-item results, keep a dead-letter store, and compare counts at the end.
- Find the real bottleneck: look at rejections, CPU, disk, throttling, and the client in turn, and change one thing at a time.
- Balance speed with safety and security: only take risks when the load can be rerun, give loaders least-privilege access, and monitor rejections and indexing throttling all the time.