Make the Collector Production-Ready: Memory Limits, Batching, and Retries
Configure processors, queues, and persistent storage so telemetry survives overload and outages.
A Collector that works on a laptop can fall over in production: a traffic spike exhausts memory, a backend outage drops data, or a restart loses everything in flight. The fixes are all configuration. This guide covers the processors and exporter settings that every production Collector should have, and the order to put them in.
The baseline pipeline
processors:
memory_limiter:
check_interval: 1s
limit_percentage: 80
spike_limit_percentage: 20
batch:
timeout: 5s
send_batch_size: 8192
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/backend]
Order matters: processors run in the order listed. memory_limiter goes first so that backpressure reaches the receivers before anything else consumes memory. batch goes last, after any filtering or enrichment, so batches are built from the final data.
memory_limiter
It watches the Collector’s memory and refuses new data when usage gets too high, so the process slows down instead of being killed by the operating system.
processors:
memory_limiter:
check_interval: 1s
limit_mib: 4000
spike_limit_mib: 800
check_intervalis how often memory is measured. A value of1sis recommended, and the default of0sdisables the periodic check.limit_mibis the hard limit for heap memory. The whole process typically uses somewhat more.spike_limit_mibis the expected maximum jump between checks, and defaults to 20 percent of the limit. The soft limit islimit_mibminusspike_limit_mib(here 3200 MiB).limit_percentageandspike_limit_percentageexpress the same limits as a share of available memory (Linux with cgroups). They suit containers where the memory limit is set by Kubernetes. Iflimit_mibis also set, it takes precedence.
Above the soft limit, the processor refuses data by returning an error to the previous component, and well-behaved senders retry or slow down. Above the hard limit it also forces garbage collection. Set it a little below the container’s memory limit so it acts before the kernel does.
batch
Batching groups items so that exports are fewer and larger, which saves network and backend work.
processors:
batch:
timeout: 5s # send after this long even if the batch isn't full
send_batch_size: 8192 # target batch size
send_batch_max_size: 0 # 0 means no upper bound
Tune timeout for latency and send_batch_size for throughput. Recent Collector versions can also batch inside an exporter’s queue; check the exporter helper documentation for your version.
Exporter queue and retries
Exporters that send over the network have a sending queue and a retry policy. They sit in the exporter, not in a processor, and they are what protects you from short backend outages:
exporters:
otlp/backend:
endpoint: backend.example.com:4317
sending_queue:
enabled: true
queue_size: 5000 # default is 1000
retry_on_failure:
enabled: true
initial_interval: 5s
max_interval: 30s
max_elapsed_time: 10m # default is 300s
How to think about the numbers:
- The queue buffers batches while the backend is slow or down. A bigger queue rides out longer incidents but uses more memory.
retry_on_failureretries with increasing delays, up tomax_elapsed_time. After that the data is dropped.- Size both for your volume and the longest outage you want to survive without loss.
Persistent queue: survive restarts
By default the queue lives in memory, so a crash loses what was queued. Back it with disk using the file_storage extension:
extensions:
file_storage:
directory: /var/lib/otelcol/storage
exporters:
otlp/backend:
endpoint: backend.example.com:4317
sending_queue:
storage: file_storage
service:
extensions: [file_storage]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp/backend]
Three pieces are required: define the extension, reference it in the exporter, and enable it under service.extensions. Mount a persistent volume at that directory, for example a PersistentVolumeClaim for a StatefulSet. Data can still be lost if the disk fills or fails, or if the backend stays down past the retry limit, so monitor disk usage.
Useful processors beyond the basics
Add or change attributes:
processors:
resource/env:
attributes:
- key: deployment.environment.name
value: production
action: upsert
attributes/scrub:
actions:
- key: http.request.header.authorization
action: delete
- key: user.email
action: hash
resource edits attributes that describe the producer (applied to a whole batch), and attributes edits per-span, per-metric-point, or per-log-record attributes. Use them to add environment labels and to remove or hash sensitive values before data leaves your network.
Drop noise with the filter processor, written in OpenTelemetry Transformation Language (OTTL):
processors:
filter/health:
error_mode: ignore
traces:
span:
- 'attributes["url.path"] == "/healthz"'
- 'attributes["url.path"] == "/ready"'
Transform data with the transform processor:
processors:
transform/normalize:
error_mode: ignore
trace_statements:
- context: span
statements:
- set(attributes["team"], "payments") where resource.attributes["service.name"] == "checkout"
OTTL is powerful, so test statements with the debug exporter on a copy of your data before enabling them in production. The statement syntax and available contexts change between versions, so check the processor’s README for yours. These processors are in the contrib distribution.
Protect the Collector itself
- Set Kubernetes resource requests and limits, and keep
memory_limiterbelow the limit. - Add
health_checkand use it for liveness and readiness probes. - Monitor the Collector’s own metrics on port 8888 for queue size, dropped items, and refused data. Alert when
otelcol_exporter_queue_sizeapproaches its capacity or when exporters report send failures. - Run more than one replica behind a Service for gateways, and size a single replica for the burst you expect.
Checklist
memory_limiteris the first processor in every pipeline.batchis the last processor.- Network exporters have a sending queue and retry policy sized for your outage tolerance.
- A persistent queue is configured where data loss is unacceptable.
- Sensitive attributes are deleted or hashed at the edge.
- Health checks, resource limits, and Collector metrics are in place.
Popular Guides
-
OpenTelemetry Explained: SDKs, OTLP, and the Collector Understand the moving parts and the three places configuration lives before you write any YAML. -
The Collector Configuration Model: Components, Pipelines, and Providers Learn the rules of the Collector's YAML: naming, pipelines, connectors, merging files, and environment variables. -
Configure SDKs with OTEL_* Environment Variables Set the service name, exporters, sampling, and limits for any language using standard variables. -
Instrument Apps Without Code Changes: Java, Python, and Node.js Attach OpenTelemetry agents with environment variables and command-line flags only. -
Deploy the Collector on Kubernetes with Helm Install the official chart, use presets for logs and metrics, and override the config through values files. -
Troubleshoot the Collector and SDKs A systematic checklist for when telemetry doesn't arrive: debug exporter, health checks, internal metrics, and common mistakes.