OpenTelemetry Collector Config Validator

Check how an OpenTelemetry Collector config is wired: which declared components no pipeline references and are therefore inert, which pipeline references something that does not exist, and whether the processors are in an order that works.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Check the wiring. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

The exporter no pipeline uses

A correctly written otlphttp exporter that no pipeline references, so it never runs. The Collector starts, validates, logs nothing, and every span goes to the debug exporter instead of the backend.

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
processors:
  batch: {}
exporters:
  debug: {}
  otlphttp:
    endpoint: https://backend.example.com
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [debug]

memory_limiter in the wrong place

Processors run in list order, so a limiter placed after batch cannot protect the memory batch already allocated.

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
processors:
  batch: {}
  memory_limiter:
    limit_mib: 512
exporters:
  debug: {}
service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, memory_limiter]
      exporters: [debug]

The renamed logging exporter

It became debug in v0.86.0 and the alias was removed in v0.92.0, so a current build refuses to start on this. Most tutorials still show it.

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
exporters:
  logging:
    verbosity: detailed
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [logging]

An OTLP receiver relying on the old default

The default endpoint moved from 0.0.0.0 to localhost, so in a container this stops accepting traffic from anywhere else while reporting itself healthy.

receivers:
  otlp:
    protocols:
      grpc:
exporters:
  debug: {}
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [debug]

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Assuming a configured component is a running component

    The top-level blocks only DECLARE. A component that no pipeline references is inert: the Collector starts, validates, logs nothing, and an unreferenced exporter means telemetry is silently discarded.

    Instead:Reference every component in a pipeline, or delete the declaration so the config says what it does.

  2. Putting `memory_limiter` anywhere but first

    Processors run in list order, so a limiter after another processor cannot protect memory that processor already allocated. The Collector is still killed by the allocation the limiter exists to prevent.

    Instead:`memory_limiter` first, `batch` last, and anything that filters or samples in between.

  3. Declaring an extension and not listing it under `service.extensions`

    Extensions are enabled by the service block. A `health_check` that is only declared does not listen, so a readiness probe against it fails and the pod never becomes ready.

    Instead:Add every extension you want running to `service.extensions`.

  4. Copying a config that uses the `logging` exporter

    It became `debug` in v0.86.0 and the alias was removed in v0.92.0, so a current build refuses to start. Most tutorials still show the old name.

    Instead:Use the `debug` exporter.

  5. Relying on the OTLP receiver's default endpoint

    It moved from `0.0.0.0` to `localhost`. In a container that stops it accepting traffic from anywhere else, and the Collector reports itself healthy while receiving nothing.

    Instead:Set `endpoint` explicitly. The default has moved once already.

Declaring a component does not switch it on

The Collector's config has two halves that look like one. The top-level blocks declare components. service.pipelines is what makes them run. A perfectly written exporter that no pipeline references starts, validates, logs nothing, and drops everything it was meant to send.

The inert component is the failure that has no symptom

There is no warning for it. The Collector reads the config, finds it valid, reports itself healthy, and the component simply never runs. For a receiver that means no data arrives. For an exporter it means data arrives and is thrown away. Both look like a problem somewhere else entirely, which is why this check leads.

exporters:
  otlphttp:      # declared
    endpoint: https://backend.example.com

service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: []   # and never used, so nothing is exported

Processor order is the order of the list

memory_limiter belongs first. A limiter placed after another processor cannot protect the memory that processor has already allocated, so the Collector can still be killed by exactly the allocation the limiter exists to prevent. batch belongs last, after anything that filters or samples, so you are not batching records you are about to discard.

Extensions are enabled by service.extensions

Declaring health_check is not enough. Until it is listed under service.extensions it does not listen, so a readiness probe pointed at it fails and the pod never becomes ready. The config looks complete and the deployment does not work.

Two components that were renamed out from under everyone

The logging exporter became debug in v0.86.0 and the alias was removed in v0.92.0, so a current build refuses to start on a config that most tutorials still show. The Jaeger receiver and exporter were removed once Jaeger adopted OTLP natively.

The OTLP receiver stopped listening on all interfaces

Its default endpoint moved from 0.0.0.0 to localhost. In a container that means it stops accepting traffic from anything outside the container, and the symptom is a Collector that is healthy and receives nothing. Set the endpoint explicitly whichever you want: the default has already moved once.

What this cannot see

It checks structure and wiring, not component settings. There are hundreds of components and their options change every release, so a bundled schema would be wrong within weeks and would be wrong silently. It cannot tell you an endpoint is unreachable, a credential is invalid, or a processor's configuration is malformed. Run otelcol validate against the real binary for that; this catches the class of mistake that binary accepts.