What Is the Memory Limiter Processor in OpenTelemetry?
The OpenTelemetry Collector sits in the middle of your telemetry traffic, and a sudden flood of data can push it out of memory and crash it. The memory limiter processor is the safety valve that watches memory use and politely turns data away before disaster strikes.
Imagine a small tea shop with ten seats and a kitchen that can prepare a fixed number of orders at once. On a normal day, customers arrive in a steady trickle and everything runs smoothly. Then a bus full of tourists arrives. If the shopkeeper accepts every single order, the kitchen overflows, cups pile up, and eventually the whole shop shuts down in chaos. A wise shopkeeper puts up a sign that says “please wait outside for a few minutes,” keeps serving the customers already inside, and reopens the door as soon as space is free. The memory limiter processor is that wise shopkeeper for your telemetry pipeline.
1The Problem: A Collector That Runs Out of Memory
Every program that receives data from outside has a danger hiding inside it: what if too much arrives at once?
Telemetry traffic is never smooth
Applications send traces, metrics, and logs to a central program called the OpenTelemetry Collector. In calm times the amount of data is predictable. But real systems have spikes. A new release can double the number of log lines. A retry storm can multiply the number of spans. A single misbehaving service can start sending metrics with thousands of new label combinations.
The Collector must hold data in memory while it processes it. It may batch the data, enrich it, or wait to send it to a backend that is temporarily slow. When data arrives faster than it leaves, the extra data piles up in memory, and the pile keeps growing.
What happens when memory runs out
Every computer has a limited amount of memory. In a container platform such as Kubernetes, each container is given a strict memory allowance. If the program inside uses more than its allowance, the platform kills it immediately. This is called an out-of-memory kill, or OOM kill. The Collector disappears without warning, and every piece of data it was holding in memory is lost.
Worse, when the Collector restarts, the senders often retry all their pending data at once. That creates another spike, which may crash the Collector again. This repeating pattern is called a crash loop, and it can leave you blind exactly when you need observability the most.
Think of a water tank with a pipe flowing in and a tap flowing out. If water comes in faster than it goes out, the tank overflows and floods the room. A float valve on the inlet pipe closes when the water level gets high, and reopens when the level falls. The memory limiter is the float valve for your Collector.
The memory limiter does not make the Collector use less memory in general. It protects the Collector from running out of memory during unusual surges by controlling how much new data is allowed in.
2A Quick Refresher on the Collector and Its Pipelines
To place the memory limiter correctly, you first need a clear picture of the Collector’s structure.
The Collector in one paragraph
The OpenTelemetry Collector is a standalone program that receives telemetry, processes it, and exports it onward. It is vendor neutral, which means it can accept data in many formats and send it to many different backends. Teams use it to avoid sending data directly from every application to every backend, and to centralize tasks such as filtering, batching, and retrying.
The four building blocks
Receiver
The front door. It accepts incoming data, such as an OTLP network call from an application, or it pulls data from somewhere, as a scraper does.
Processor
A workshop station in the middle. It can change, filter, group, or limit data as it passes through.
Exporter
The back door. It sends the finished data to a destination such as a tracing backend, a metrics store, or a log platform.
Pipeline
The assembly line that connects receivers, then processors in order, then exporters, for one type of signal such as traces, metrics, or logs.
Order matters
Inside a pipeline, processors run one after another in the order you list them. The output of the first becomes the input of the second. This ordering is not a minor detail. It decides what each processor sees and what protection each one can offer. For the memory limiter, the order is especially important, as later chapters will show.
flowchart LR
R["Receivers"] --> P1["Processor 1"]
P1 --> P2["Processor 2"]
P2 --> P3["Processor 3"]
P3 --> E["Exporters"]
A pipeline is like an airport. Receivers are the entrance doors, processors are the security checks and passport counters in a fixed order, and exporters are the departure gates. A guard at the entrance can stop a crowd from flooding the checkpoints.
3So, What Is the Memory Limiter Processor?
A plain definition first, then the behavior that makes it useful.
The definition
The memory limiter processor is a component of the OpenTelemetry Collector that periodically measures how much memory the Collector is using and, when that memory gets too high, starts refusing new data so that the Collector can recover before it crashes. It is designed to prevent out-of-memory situations caused by sudden surges of incoming telemetry.
The three things it does
It watches
At regular short intervals, it checks the current memory use of the Collector process and compares it to the limits you configured.
It refuses
When memory passes a warning level called the soft limit, it begins rejecting incoming data, sending an error back toward the receiver instead of passing the data down the pipeline.
It forces cleanup
When memory passes the harder, higher limit, it asks the runtime to run garbage collection, which means cleaning up memory that is no longer needed, to bring usage down faster.
What it is not
It is not a data filter that chooses which telemetry is important. It does not look at the content of spans, metrics, or logs. It makes no decisions based on what the data says, only on how full memory is. It is also not a replacement for giving the Collector enough memory in the first place, and not a replacement for scaling out when your normal load grows.
Some people think the memory limiter caps memory like a strict ceiling that the process can never cross. It does not. It is a reactive guard that checks at intervals and reacts. Memory can still rise between checks, which is why the settings leave a safety margin.
4A Gentle Look at How the Collector Uses Memory
You do not need to be a systems expert, but a few simple ideas make the settings much easier to understand.
The Collector is written in Go
The Collector is a program written in the Go language. Go manages memory automatically. It hands out memory to your data as needed and has a background cleaner, called the garbage collector, that finds memory no longer in use and frees it. This is convenient, but it means memory use is not always a precise, instant number. The cleaner runs from time to time, not constantly.
The heap
The part of memory where the program stores most of its changing data, such as batches of spans waiting to be sent, is called the heap. The memory limiter looks at the heap size when it measures. Besides the heap, the process also needs some extra memory for itself, such as program code and internal bookkeeping, so the total used by the container is somewhat higher than the heap alone.
Where the memory goes inside a Collector
Receiver buffers
Incoming requests are decoded and held briefly before being passed on. Large or numerous requests mean larger buffers.
Batch processor
Telemetry waits here until enough has gathered or a timer fires, so that it can be sent in efficient groups.
Exporter queues
When a backend is slow or down, exporters keep data in a queue and retry. This is often the biggest memory consumer during outages.
Stateful processors
Some processors, such as those that group spans into whole traces or compute metric deltas, must remember data for a while.
Think of a kitchen counter. Dishes waiting to be cooked, dishes waiting to be served, and dishes waiting to be washed all take up counter space. A cleaner comes by now and then to clear it. If dishes arrive faster than the cleaner and the cooks can handle them, the counter fills up and nothing else fits.
The memory limiter does not know which of these places is using the memory. It looks only at the total. That is why it works well as a general protector regardless of the cause of the pressure.
5Soft Limit, Hard Limit, and Spike Limit
The processor works with two thresholds and a safety margin. Understanding them is the heart of the topic.
The hard limit
The hard limit is the maximum amount of memory, in mebibytes, that you want the Collector process heap to use. A mebibyte is a standard unit of memory slightly larger than a megabyte. When measured memory goes above the hard limit, the processor not only refuses data but also triggers garbage collection to push memory back down.
The spike limit
The spike limit is a safety margin. It estimates how much memory could suddenly be added between two checks. If the Collector is receiving data very fast, memory could jump a lot in one second. The spike limit is the room you keep in reserve for that jump. If you do not set it, a default of about twenty percent of the hard limit is commonly used.
The soft limit
The soft limit is not set directly. It is calculated as the hard limit minus the spike limit. Once memory goes above the soft limit, the processor starts refusing new data. Because the soft limit sits below the hard limit, refusing starts early enough that the spike margin can absorb data still in flight.
| Threshold | How it is set | What happens when memory crosses it |
|---|---|---|
| Soft limit | Hard limit minus spike limit | New data is refused until memory falls back below this level |
| Hard limit | Configured directly | Data is refused and garbage collection is forced to reduce memory |
| Spike limit | Configured, or a default share of the hard limit | Acts as the safety margin that defines the soft limit |
stateDiagram-v2
[*] --> Normal
Normal --> Refusing: Memory above soft limit
Refusing --> Normal: Memory below soft limit
Refusing --> ForcedGC: Memory above hard limit
ForcedGC --> Refusing: Garbage collection done, still above soft limit
ForcedGC --> Normal: Memory drops below soft limit
A worked example in words
Suppose you set the hard limit to 1,000 mebibytes and the spike limit to 200 mebibytes. The soft limit is then 800 mebibytes. While memory stays under 800, everything flows normally. When memory climbs past 800, the processor begins refusing incoming data. If memory somehow still climbs past 1,000, it also forces garbage collection. When memory falls back under 800, data is accepted again.
A lift has a rule that says “stop letting people in at 8 passengers” even though the true limit is 10. The two empty spots are the safety margin for people already running toward the doors. The soft limit is 8, the hard limit is 10, and the spike margin is 2.
Setting the spike limit larger than or equal to the hard limit makes no sense, because the soft limit would become zero or negative. The processor is designed to reject such configuration at startup.
6The Settings You Can Tune
There are only a few settings, which is part of what makes the processor easy to adopt.
check_interval
How often memory is measured. A common recommendation is about one second. Spiky traffic benefits from shorter intervals, because there is less time for memory to jump unnoticed.
limit_mib
The hard limit as a fixed number of mebibytes. Use this when you know exactly how much memory the Collector gets.
spike_limit_mib
The safety margin as a fixed number of mebibytes. The soft limit equals the hard limit minus this value.
limit_percentage
The hard limit expressed as a percentage of the total memory available to the process, such as the container allowance. Handy when the same configuration runs on machines of different sizes.
spike_limit_percentage
The safety margin expressed as a percentage of the total available memory, used together with the percentage-based hard limit.
Fixed numbers versus percentages
You choose between two styles. Fixed numbers are simple and predictable, and they are good when you control the container size. Percentages adapt automatically to the memory the process can see, which is helpful when the same configuration file is shared across environments with different resource sizes. You should pick one style for the hard limit and not mix the two for the same threshold.
| Style | Best for | Watch out for |
|---|---|---|
| Fixed mebibytes | Stable environments with a known container size | Forgetting to update the limit when the container size changes |
| Percentage | Shared configuration across varied machine sizes | Percentages are based on what the process detects as available, so verify detection in your platform |
Exact setting names, default values, and which settings are required can change between Collector versions. Always confirm the details in the current Collector documentation for the version you run.
7Back-pressure: What Happens to Refused Data
Refusing data sounds simple, but what the sender does next decides whether you lose anything.
What refusing really means
When the memory limiter is in refusing mode, it does not quietly throw data away. It returns an error up the chain, back to the receiver that handed it the data. The receiver is then expected to tell the sender that the Collector is temporarily unable to accept the data. This signal going backwards against the flow of data is called back-pressure.
Why back-pressure is a good thing
Back-pressure moves the problem to the place that is best able to handle it. Instead of the Collector crashing and losing everything, the sender, which has the data, is told to wait. Many senders keep the data in a small buffer and retry after a short delay. By the time they retry, the Collector has recovered.
It depends on the receiver and the sender
How well this works depends on both ends. A push-style receiver, such as one accepting OTLP calls, can return a retryable error to the application’s exporter. A pull-style receiver, which scrapes data from targets, simply skips or fails that scrape, and the data for that moment may be lost, although the next scrape will usually succeed.
| Receiver style | What the memory limiter’s refusal causes | Is data lost? |
|---|---|---|
| Push receiver with retrying clients | Client gets a retryable error and tries again later | Usually no, if the client buffers and retries long enough |
| Push receiver with non-retrying clients | Client gets an error and drops the data | Yes, on the client side |
| Pull receiver, such as a scraper | That collection round fails or is skipped | Possibly a gap for that interval |
| Receiver reading from a message queue | Consumption slows and messages stay in the queue | Usually no, the queue acts as a buffer |
sequenceDiagram
participant A as Application Exporter
participant R as Receiver
participant M as Memory Limiter
participant X as Rest of Pipeline
A->>R: Send batch of telemetry
R->>M: Pass data to first processor
M->>M: Check memory state
alt Memory below soft limit
M->>X: Accept and forward
X-->>A: Success
else Memory above soft limit
M-->>R: Refuse with error
R-->>A: Retryable error response
Note over A: Wait and retry later
end
Refusing is only safe if senders are configured to retry. Check the retry and buffering settings in your SDKs and agents. A sender that drops data on the first error turns every memory pause into permanent data loss.
A post office that is overloaded can say “please come back in ten minutes,” and customers keep their parcels. That is far better than the post office silently shredding parcels or collapsing from overload.
8Where to Place It: The First Processor Rule
If you remember one placement rule, remember this one.
The recommendation
The memory limiter should be the first processor in each pipeline, placed immediately after the receivers. This is the standard guidance in the Collector documentation, and the reasons are practical.
Refused data never enters the pipeline
If the limiter is first, data that is rejected does no further work. It is not decoded further, enriched, or batched, which saves memory right at the source.
The error returns directly to the receiver
Because nothing has happened in between, the refusal reaches the receiver quickly and cleanly, so the sender can react without delay.
Memory-hungry processors are protected
Processors that hold data, such as batching or trace grouping, come after the limiter, so they only receive data that the Collector has room for.
What goes wrong with bad placement
If the limiter is placed after a batching processor, the batcher may already have accepted and stored a large amount of data before the limiter gets a chance to say no. The data in the batcher still occupies memory, and the limiter can only protect against data that has not yet reached it. In effect, you have put the guard inside the shop instead of at the door.
flowchart LR
R["Receiver"] --> M["Memory limiter first"]
M --> A["Attribute and filter processors"]
A --> B["Batch processor"]
B --> E["Exporter"]
Put the memory limiter first, and put the batch processor last among processors, just before exporters. Everything else goes in between.
Each pipeline that handles a signal type, such as traces, metrics, or logs, should include the limiter. Protecting only one pipeline leaves the others exposed, and memory pressure in any pipeline affects the whole process.
A bouncer works at the club entrance, not at the dance floor. By the time guests are on the dance floor, it is too late to decide the room is full.
9Choosing the Right Numbers
Good settings come from understanding your container, your traffic, and your safety margins.
Start from the container allowance
The first number to know is the total memory your Collector container or machine is allowed to use. The memory limiter’s hard limit must sit comfortably below that total. This is because the process uses extra memory beyond the heap, and because the limiter reacts with a small delay. If the hard limit equals the container allowance, an OOM kill can still happen before the limiter acts.
A common guideline
A widely used rule of thumb is to set the hard limit at roughly eighty percent of the container’s memory allowance, and the spike limit at roughly twenty to twenty-five percent of the hard limit. This leaves a buffer for non-heap memory and for the fast spikes between checks. Treat these as starting points, then adjust using real measurements from your environment.
| Container memory | Suggested hard limit (about 80 percent) | Suggested spike limit (about 20 to 25 percent of hard) | Resulting soft limit |
|---|---|---|---|
| 512 MiB | About 400 MiB | About 80 to 100 MiB | About 300 to 320 MiB |
| 1,024 MiB | About 800 MiB | About 160 to 200 MiB | About 600 to 640 MiB |
| 2,048 MiB | About 1,600 MiB | About 320 to 400 MiB | About 1,200 to 1,280 MiB |
| 4,096 MiB | About 3,200 MiB | About 640 to 800 MiB | About 2,400 to 2,560 MiB |
Match the spike limit to your traffic pattern
If your traffic is steady, a smaller spike margin is fine. If your traffic arrives in sharp bursts, for example from batch jobs that send large payloads at once, a larger margin or a shorter check interval is wise. The goal is that the largest realistic jump in memory between two checks fits inside the spike margin.
Example: A bursty logging gateway
A team runs a gateway Collector that receives logs from many services. Every hour, a scheduled job flushes a large number of log lines in a few seconds. They find that memory occasionally touches the hard limit. They respond by shortening the check interval and enlarging the spike margin, and the container stops being killed.
Test your settings by sending synthetic bursts at the Collector in a staging environment. Watch whether it enters refusing mode and recovers before the container is killed. Numbers that look right on paper may behave differently under real load.
When packing a suitcase for a flight with a weight limit, you do not aim for exactly the limit. You leave some room, because souvenirs may be added on the way back. The gap between your target and the real limit is your safety margin.
10Working Alongside the Garbage Collector, Batching, and Queues
The memory limiter is one safety tool among several, and it works best in combination.
The runtime memory limit setting
Go programs can be given a soft memory target through a standard environment variable named GOMEMLIMIT. When set, the Go runtime tries to garbage collect more aggressively as the process approaches that target. Many operators set this to a value a little below the container allowance, and some also align it with the memory limiter’s hard limit. It complements the limiter by making cleanup happen sooner, while the limiter controls the inflow of data. Confirm the current recommended practice in the Collector documentation for your version.
The old ballast approach
Older guides mention a memory ballast, which was a large block of unused memory allocated on purpose to change how the garbage collector behaved. That approach has been replaced by the runtime memory limit setting, and recent Collector versions have moved away from it. If you find tutorials describing ballast configuration, treat them as outdated and check current guidance.
Batch processor
The batch processor groups data before sending. Larger batches and longer wait times mean more data held in memory. It is wise to keep batch sizes reasonable and to place the batch processor after the memory limiter, so the limiter can control how much data arrives to be batched.
Exporter queues and retries
Exporters often have a sending queue that holds data while a backend is slow, and retry settings that decide how long to keep trying. A large queue can become the main consumer of memory during an outage. The queue size should be chosen with the memory budget in mind. Where durability matters, some deployments store the queue on disk through a storage extension, which reduces memory pressure and survives restarts.
Memory limiter
Controls how much new data is accepted, based on current memory use.
Runtime memory target
Makes the garbage collector work harder as memory nears a chosen target.
Bounded batch and queue sizes
Limits how much data the Collector may hold in internal buffers at any time.
Disk-backed queues
Moves waiting data out of memory when durability and memory savings are both needed.
Horizontal scaling
Adds more Collector instances when the normal load, not just spikes, outgrows one instance.
Relying on the memory limiter alone while leaving exporter queues enormous. The limiter helps, but if queues can hold more than the whole memory budget, the system will spend most of its time refusing data. Size queues and limits together.
Preventing a flood uses several measures together: a dam, drainage channels, and a warning siren. The memory limiter is the siren-and-gate at the entrance, while queue limits and cleanup settings are the drainage.
11A Day in the Life: Surviving a Traffic Spike
Let us follow a realistic spike from the first signs to full recovery.
The scenario
A retail company runs a gateway Collector with a memory limiter configured as the first processor. A flash sale begins, and the volume of traces triples in a few minutes. At the same time, the tracing backend slows down because it is also under load.
Traffic rises
More spans arrive than the exporters can send. The sending queue begins to fill, and memory rises steadily.
Soft limit is crossed
At the next check, the limiter sees that memory has gone past the soft limit. It switches to refusing mode and starts returning errors to the receiver.
Applications back off
Application exporters receive retryable errors. They keep the data in their own buffers and retry after short delays, so the inflow to the Collector drops.
The Collector catches up
With less new data coming in, exporters drain the queue as the backend recovers. Garbage collection frees released memory, and usage falls.
Normal service resumes
When memory drops below the soft limit, the limiter stops refusing. Retried data flows in at a manageable pace, and the Collector never crashed.
flowchart TD
S["Spike in incoming data"] --> Q["Queues fill, memory rises"]
Q --> C{"Above soft limit?"}
C -- "No" --> N["Keep accepting data"]
C -- "Yes" --> R["Refuse new data with errors"]
R --> B["Senders retry later"]
B --> D["Queues drain, memory falls"]
D --> C
What would have happened without the limiter
Without the memory limiter, the queue would have kept growing until the container exceeded its allowance. The platform would have killed the Collector, and all queued data would have vanished. When it restarted, every application would have retried at once, and the cycle might have repeated. The limiter converts a dangerous crash into a calm pause.
Notice that the system recovered by itself because all participants behaved well: the limiter refused politely, senders retried, and the backend eventually caught up. If any participant misbehaves, such as a sender that does not retry, the benefit is reduced.
12Monitoring the Memory Limiter
A safety valve that no one watches is a mystery. You should know when yours is doing its job.
Signals worth watching
The Collector exposes its own internal metrics, which describe its health. The memory limiter contributes counters that show how much data it has refused, separately for spans, metric points, and log records. A rising refusal count is a direct sign that the Collector is under memory pressure and sending back-pressure to your applications.
| Signal | What it tells you | How to react |
|---|---|---|
| Refused spans, metric points, or log records rising | The limiter is actively rejecting data | Investigate the cause, then add capacity or tune limits |
| Process memory near the hard limit often | The Collector is regularly close to its ceiling | Increase memory or scale out, and review queue sizes |
| Exporter queue size near capacity | A backend is slow or unreachable | Check backend health and exporter retry settings |
| Container restarts or OOM kills | The limiter was too lenient or reacted too late | Lower the hard limit, enlarge the spike margin, shorten the check interval |
| Client-side dropped data | Senders are not retrying enough | Adjust retry and buffer settings in SDKs and agents |
Alerting suggestions
Create alerts on a sustained non-zero refusal rate, not just a single blip, because brief refusals during a spike are normal and healthy. Alert separately on any container restart for the Collector, since that indicates the limiter did not do its job. Combine these with dashboards that show memory, queue sizes, and refusals side by side, so the story is easy to read.
If your Collector sends its own metrics to itself, a memory crisis may interrupt the very data you need to diagnose it. Consider sending Collector self-monitoring data through a separate, small, stable path.
A pressure relief valve on a boiler is useful, but only if there is a gauge that tells you when it opens. Frequent opening means the boiler is working too hard and needs attention.
13Deployment Patterns and the Memory Limiter
Where you run your Collector changes how you think about memory protection.
Agent pattern
In the agent pattern, a small Collector runs next to each application, for example on every host or as a sidecar container. It receives data from nearby applications and forwards it onward. These Collectors usually have small memory allowances, so the limiter is especially valuable to keep them from competing with the application for resources.
Gateway pattern
In the gateway pattern, a central pool of Collectors receives data from many sources. These instances carry the most traffic and the most risk of sudden spikes. They benefit from the limiter, from careful queue sizing, and from the ability to scale horizontally behind a load balancer.
Why one limiter is not enough for scale
The limiter protects a single instance. It does not coordinate across instances. If one instance is refusing data, a load balancer might still send new traffic to it, unless it knows the instance is struggling. Good gateway designs combine the limiter with health checks, sensible load balancing, and automatic scaling based on resource use.
Agent next to the app
Small footprint, protects the host. Refused data goes back to the local application, which can buffer briefly.
Central gateway pool
Large traffic, shared by many senders. Needs scaling plus the limiter, and depends on retrying clients.
Tiered agent plus gateway
Agents refuse toward applications, gateways refuse toward agents. Each tier absorbs pressure in turn.
Example: Two-tier protection
A platform team runs small agents on each node and a gateway pool behind a load balancer. During a backend outage, the gateways fill and start refusing. The agents, which also have limiters and modest queues, pass the back-pressure to applications. The pressure spreads across tiers instead of causing a crash at one point.
Refusing data is a short-term defense. If the Collector refuses data most of the day, the real fix is more capacity, not looser limits.
14Benefits, Trade-offs, and Security Angle
Like every tool, the memory limiter has strengths and costs.
A defense against overload and abuse
Because the limiter reacts to memory pressure regardless of the cause, it also gives some protection against abusive or accidental floods. A misconfigured service or a malicious sender that blasts huge amounts of data at the Collector cannot force it to consume unlimited memory. The limiter will start refusing before the process crashes. It is not a complete security control, but it is a helpful safeguard for availability.
Advantages
- Prevents out-of-memory crashes during sudden traffic spikes
- Turns crashes into recoverable pauses using back-pressure
- Simple to configure with only a few settings
- Works regardless of which component is using the memory
- Protects all signal types when added to every pipeline
- Gives some resilience against accidental or abusive floods
Disadvantages / Trade-offs
- Refused data may be lost if senders do not retry
- Checks happen at intervals, so very fast spikes can slip past
- Does not fix a chronic shortage of memory or capacity
- Works per instance and does not coordinate across a fleet
- Poor placement or poor numbers weaken its protection
The limiter cannot take back memory that is already in use by data it accepted earlier. It only controls new arrivals. That is why the soft limit sits below the hard limit, and why other safety measures such as bounded queues are still necessary.
A fuse in an electrical circuit prevents damage during a surge, but it does not give you more electricity. If the fuse trips every day, you need a bigger supply or fewer appliances, not a stronger fuse.
15Troubleshooting, Best Practices, and Anti-patterns
A short field guide for when things do not behave as expected.
Troubleshooting guide
| Symptom | Likely cause | What to check |
|---|---|---|
| Collector is killed by the platform despite having a limiter | Hard limit too close to container allowance, or spikes too fast | Lower the hard limit, enlarge the spike margin, shorten the check interval |
| Limiter refuses data constantly | Limits too low for normal load, or a slow backend | Compare normal memory use to the soft limit, and check exporter health |
| Data is missing in the backend during spikes | Senders drop data on refusal | Enable retries and buffering in SDKs and agents |
| Memory stays high even when idle | Large queues or retained data in other components | Review queue sizes, batch settings, and stateful processors |
| Limiter seems to have no effect | Placed late in the pipeline or missing from some pipelines | Confirm it is the first processor in every pipeline |
| Configuration fails to start | Spike limit not smaller than the hard limit, or conflicting settings | Check threshold values and avoid mixing fixed and percentage styles |
Habits that work
Always include it
Add the memory limiter to every production pipeline as the first processor, even when traffic seems small.
Leave headroom
Set the hard limit comfortably below the container allowance, and size the spike margin to your burstiest traffic.
Configure retrying senders
Make sure SDKs and upstream agents retry on temporary errors, so that refusals do not become data loss.
Bound the queues
Choose exporter queue and batch sizes that fit within the memory budget, so the limiter is not constantly fighting them.
Monitor refusals
Track refusal counters, memory, and restarts, and alert on sustained patterns rather than single events.
Load test regularly
Rehearse spikes in a safe environment so you learn how the system behaves before real incidents.
Traps to avoid
Problem
The memory limiter is placed after the batch processor or other memory-holding processors in the pipeline.
Why It’s Harmful
Data is already accepted and stored by earlier processors before the limiter can refuse it. The protection arrives too late to prevent the pile-up.
Correct Approach
Place the limiter first, immediately after the receivers, in every pipeline.
Problem
The hard limit is set equal to, or very close to, the container memory allowance.
Why It’s Harmful
The process uses memory beyond the heap, and the limiter reacts only at intervals. The platform may kill the container before the limiter ever acts.
Correct Approach
Keep the hard limit well below the allowance, and keep a spike margin based on real traffic bursts.
Problem
Teams raise the limits again and again whenever refusals appear, without investigating why memory is high.
Why It’s Harmful
The underlying cause, such as a slow backend, huge queues, or growing traffic, remains. Eventually the limits run into the container’s physical ceiling and crashes return.
Correct Approach
Treat frequent refusals as a signal. Find the cause, then scale out, fix the backend, or resize queues, and only then retune the limits.
Problem
Senders are configured without retries, or with very short buffers, while the Collector is expected to refuse data under pressure.
Why It’s Harmful
Every refusal becomes permanent loss. The system appears to protect the Collector but silently damages data completeness.
Correct Approach
Enable sensible retry and buffering behavior at the sender, and test the whole path with a deliberate overload.
16Frequently Asked Questions
Quick answers to the questions people ask most often.
It is not forced by the software, but it is strongly recommended for any production Collector. Without it, a surge of data can push the process beyond its memory allowance and cause a crash.
It refuses to accept new data when memory is high, by returning an error to the receiver. Whether data is actually lost depends on the sender. If the sender retries, the data is usually delivered later. If the sender does not retry, the data may be lost.
The soft limit is the point at which the processor starts refusing new data. It equals the hard limit minus the spike limit. The hard limit is the higher ceiling at which the processor also forces garbage collection to reduce memory.
Placing it first means refused data does no further work and consumes no extra memory, and the refusal reaches the receiver quickly. Later processors, which may hold data, receive only what the Collector has room for.
Yes. The processor supports percentage-based settings relative to the memory available to the process. This is useful when the same configuration is used on machines or containers of different sizes. Confirm the exact behavior for your Collector version.
A check interval of around one second is the usual recommendation. If your traffic is very spiky, a shorter interval or a larger spike margin reduces the chance of memory overshooting between checks.
No. It protects one instance against short surges. If your normal load regularly approaches the limits, you need more memory per instance or more instances.
It is good practice on both. Agents keep local resource use in check, and gateways handle the largest and spikiest loads. Together they allow back-pressure to flow smoothly through each tier.
Watch the Collector’s own metrics for refused spans, metric points, and log records, along with memory use, exporter queue sizes, and container restarts. Occasional refusals during spikes show the limiter doing its job. Container kills show it is not enough.
Yes, the processor is designed to work with traces, metrics, and logs. You add it to each pipeline you run so that all signal types are protected.
17Summary and Key Takeaways
The memory limiter processor is a small but vital part of a healthy OpenTelemetry Collector. It watches memory at regular intervals, refuses new data when memory passes a soft limit, and forces cleanup when memory passes a hard limit. By sending errors back to receivers, it creates back-pressure that gives senders a chance to wait and retry, turning a possible crash into a short pause. It works best as the first processor in every pipeline, with limits set comfortably below the container allowance, with sensible queue sizes, and with senders that retry. It is a guard against surges, not a cure for chronic under-provisioning, so monitor refusals and scale when the pressure is constant rather than occasional.
Key Takeaways
- It protects the Collector from out-of-memory crashes — it measures memory at intervals and refuses new data when usage is too high.
- Two thresholds and a margin — the soft limit, equal to the hard limit minus the spike limit, starts refusals; the hard limit also forces garbage collection.
- Refusal creates back-pressure — errors flow back to receivers and senders, so retrying senders usually deliver the data later without loss.
- Place it first in every pipeline — right after the receivers, so refused data costs nothing and memory-holding processors are protected.
- Leave headroom below the container limit — a common starting point is about eighty percent for the hard limit, then tune with real measurements.
- It works with other safeguards — combine it with a runtime memory target, bounded batches and queues, and horizontal scaling.
- Monitor and act on refusals — occasional refusals are healthy, but constant refusals mean you need more capacity or a fix elsewhere in the pipeline.