Building resilient OpenTelemetry Collector pipelines: batching, buffering, and persistence

Observability data is useful only if it arrives intact and on time. When you run an OpenTelemetry Collector as the central routing layer for traces, metrics, and logs, design choices around batching, queueing, memory management, and persistence determine whether you lose data during outages or create instability under load. This article walks through practical patterns and a compact Collector config that balance throughput, cost, and reliability.

The goal: durability and stability, not just throughput

Collectors sit between noisy instrumented applications and often-constrained backends. A resilient pipeline should:

The Collector is intentionally opinionated about observability: it emits its own internal telemetry (logs, metrics) that let you spot exporter failures and dropped items (for example: exporter send-failure counters). Watching those internal metrics gives early warning that your pipeline is struggling. (opentelemetry.io)

Core building blocks

Use these Collector features together — they are complementary.

Together these reduce dropped data while keeping the Collector stable under load. (opentelemetry.io)

Why disk-backed queues matter

In-memory queues are fast but fragile: if the Collector process restarts or a pod is evicted, queued telemetry can disappear. The Collector supports a storage-backed persistent queue (commonly configured with the file_storage extension) so queued items are written to disk and resumed after restart. That persistence uses an embedded store and is a practical safeguard against transient network partitions and process restarts. (go.opentelemetry.io)

Processor order affects behavior. A pragmatic ordering that many operators use:

  1. resourcedetection (enrich early)
  2. memory_limiter (protect the process)
  3. sampling/filtering/transforms (reduce cardinality before expensive batch work)
  4. batch (group items efficiently)
  5. queued_retry / exporter queue (buffer for exporter retries)

Placing memory_limiter before sampling and batching ensures you can shed or throttle before doing heavy work; placing batch after sampling avoids batching data that will later be dropped or changed. (opentelemetry.io)

Quick example: compact Collector YAML

This snippet focuses on a resilient traces pipeline with a persistent exporter queue. Adapt names and addresses for your environment.

extensions:
  file_storage:
    directory: /var/lib/otelcol/persistent-queue

processors:
  memory_limiter:
    limit_percentage: 75
    spike_limit_percentage: 20
  batch:
    send_batch_size: 512
    timeout: 5s
  queued_retry:
    num_workers: 4
    queue_size: 5000

service:
  extensions: [file_storage]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch, queued_retry]
      exporters: [otlp/mybackend]

exporters:
  otlp/mybackend:
    endpoint: otlp.backend.example:4317
    retry_on_failure: true
    sending_queue:
      enabled: true
      storage: file_storage/otc

Notes:

Sampling and tail-based sampling caveats

Tail-based sampling (decide after seeing a whole trace) is attractive for preserving important traces while reducing volume, but it is stateful and can be a scaling bottleneck for large fleets. If you plan to sample at the Collector, evaluate the memory and coordination needs (and consider external sampling services or scalable tail-sampling implementations) so the Collector doesn’t become the bottleneck. (github.com)

What to monitor on the Collector itself

Treat the Collector as a first-class service and monitor its internal telemetry:

Sustained exporter-failure metrics or rising queue-drop counts indicate you should either scale the Collector (horizontal replicas for ingestion) or reduce incoming volume (sampling/filters). The Collector’s internal metrics make these signals visible. (opentelemetry.io)

Pitfalls and practical tips

Closing summary

A resilient OpenTelemetry Collector pipeline balances batching, queueing, memory protection, and — when needed — disk-backed persistence. Use memory_limiter early to protect the process, batch after sampling to reduce overhead, and enable a persistent queue if your environment can’t tolerate the risk of in-memory loss. Instrument and monitor the Collector’s own telemetry so you can see when the pipeline is strained and respond before data is permanently lost. (opentelemetry.io)