GuidesOpenTelemetry Collector

OpenTelemetry Collector setup and configuration

Updated 17 October 2026 · 9 minute read

The short answer: run the otel/opentelemetry-collector-contrib image with a config.yaml that has an otlp receiver, the memory_limiter and batch processors, and an otlphttp exporter pointed at your backend. Then point your applications at the Collector on port 4317 (gRPC) or 4318 (HTTP). This guide explains each part, gives a configuration you can copy, and shows how to check that data is flowing.

OpenTelemetry and OTLP in one minute

OpenTelemetry is an open standard and a set of libraries for producing traces, metrics and logs. You instrument your code once, with an SDK for your language, and you can send the data to any backend that accepts it.

OTLP (the OpenTelemetry Protocol) is the wire format. It runs over gRPC, usually on port 4317, or over HTTP, usually on port 4318, with paths /v1/traces, /v1/metrics and /v1/logs. Most observability backends accept OTLP directly.

SDK direct export or a Collector?

You have two choices. An SDK can export straight to your backend, or it can export to a Collector that you run, which then forwards the data.

SDK direct exportThrough a Collector
SetupSet a few environment variablesRun and maintain one more component
CredentialsEvery service holds the backend tokenOnly the Collector holds it
ProcessingLimited to what the SDK offersFilter, redact, enrich and sample centrally
Retries and bufferingInside each application processMoved out of the application
Changing backendRedeploy every serviceChange one file

Direct export is fine for a few services. A Collector pays off when you have many services, several languages, or rules you want to apply in one place.

How the Collector is built

The Collector is a single binary configured by one YAML file. The file has four main sections.

  • Receivers take data in. The otlp receiver accepts OTLP from SDKs. Others scrape Prometheus endpoints, read log files or collect host metrics.
  • Processors change data in transit: batching, memory protection, adding or removing attributes, filtering and sampling.
  • Exporters send data out, for example otlphttp or otlp (gRPC) to a backend, or debug to the Collector's own log.
  • Pipelines, under service, connect them. Each pipeline handles one signal (traces, metrics or logs) and lists its receivers, processors in order, and exporters.

A component does nothing until a pipeline uses it. Defining an exporter and forgetting to add it to a pipeline is a common reason why no data arrives.

There are two distributions. The core image contains a small set of components. The contrib image adds many more receivers, processors and exporters, and is what most teams use.

A working config.yaml

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 400
    spike_limit_mib: 100
  batch:
    send_batch_size: 1000
    send_batch_max_size: 2000
    timeout: 5s

exporters:
  otlphttp/backend:
    endpoint: https://otlp.example.com
    headers:
      Authorization: "Bearer ${env:BACKEND_TOKEN}"
  debug:
    verbosity: basic

extensions:
  health_check:
    endpoint: 0.0.0.0:13133

service:
  extensions: [health_check]
  pipelines:
    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/backend]
    metrics:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/backend]
    logs:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/backend]

Points worth knowing:

  • Listen on 0.0.0.0 in containers. Recent Collector versions default the receiver to localhost, which other containers cannot reach.
  • Put memory_limiter first. It refuses data when the Collector nears its memory limit, so the process slows down instead of being killed. Set limit_mib below the container's memory limit.
  • Put batch after it. Sending fewer, larger requests uses less CPU and network. send_batch_max_size caps the size of each request, which matters when your backend limits request size.
  • Keep secrets out of the file. ${env:BACKEND_TOKEN} reads the token from an environment variable when the Collector starts.
  • The otlphttp exporter adds the paths itself. Give it the base URL; it sends to /v1/traces, /v1/metrics and /v1/logs, gzip-compressed by default.
  • The /backend suffix is a name. type/name lets you define two exporters of the same type, for example to send to two places during a migration.

Run it with Docker

docker run -d --name otelcol \
  -p 4317:4317 -p 4318:4318 -p 13133:13133 \
  -e BACKEND_TOKEN=your-token \
  -v "$(pwd)/config.yaml:/etc/otelcol-contrib/config.yaml:ro" \
  otel/opentelemetry-collector-contrib:<version>

Pin a specific version tag rather than latest, so an upgrade happens when you choose. The contrib image reads its configuration from /etc/otelcol-contrib/config.yaml. Check that it started:

docker logs otelcol
curl -s http://localhost:13133/

The log should end with a line saying that everything is ready, and the health check should answer. On Kubernetes, the same image and file usually run as a DaemonSet (one Collector per node, close to the applications) or a Deployment (a central gateway). Many teams use both: node Collectors forward to a central one that holds the credentials.

Point your applications at the Collector

OpenTelemetry SDKs read a standard set of environment variables, so you rarely need code changes.

export OTEL_SERVICE_NAME=checkout-api
export OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4318
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.version=1.4.2
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.25
VariableWhat it does
OTEL_SERVICE_NAMENames the service. Set it for every service; without it, data shows up as unknown_service.
OTEL_EXPORTER_OTLP_ENDPOINTWhere to send. For HTTP, the SDK adds /v1/traces and the other paths.
OTEL_EXPORTER_OTLP_PROTOCOLgrpc or http/protobuf. Defaults differ between SDKs, so set it.
OTEL_EXPORTER_OTLP_HEADERSExtra headers as key=value pairs. Needed when exporting directly to a backend; usually not needed towards your own Collector.
OTEL_RESOURCE_ATTRIBUTESAttributes added to everything the service sends, such as environment and version.
OTEL_TRACES_SAMPLER and _ARGWhich sampler to use and its setting, for example a ratio of 0.25.

Sampling basics

Busy services produce more traces than anyone reads. Sampling keeps a share of them.

  • Head sampling decides when a trace starts. parentbased_traceidratio with 0.25 keeps about a quarter of new traces, and child services follow the parent's decision, so traces stay complete. It is cheap but blind: it cannot know in advance which requests will fail.
  • Tail sampling decides after the trace has finished, in the Collector, with the tail_sampling processor in the contrib image. You can keep every trace with an error or a slow span and a small share of the rest. It needs all spans of a trace to reach the same Collector instance, and it holds traces in memory while it waits.

Start with no sampling while volumes are small. Add head sampling when cost or volume makes it necessary, and tail sampling when you need to keep every error.

Troubleshooting

When nothing arrives, find where the data stops. Add the debug exporter to a pipeline next to your real exporter:

    traces:
      receivers: [otlp]
      processors: [memory_limiter, batch]
      exporters: [otlphttp/backend, debug]

With verbosity: detailed it prints every span to the Collector's log. If spans appear there, the applications and receiver are fine and the problem is the export. If they do not, look at the application side.

  • Connection refused — wrong port, the receiver is listening on localhost, or the port is not published from the container.
  • Protocol mismatch — an SDK sending gRPC to port 4318, or HTTP to 4317.
  • 401 or 403 from the backend — missing or wrong token, or the environment variable was not passed to the container.
  • 413 or a size error — lower send_batch_max_size.
  • Refused data and memory warnings — the memory limiter is working; give the Collector more memory or run more instances.
  • Invalid configuration — the Collector exits at start-up and logs the line it could not parse. YAML indentation is the usual cause.

Remove the debug exporter, or set it back to basic, once you are done; detailed output is large.

Send OpenTelemetry data to Televane

Televane accepts traces, metrics and logs over OTLP, so the Collector setup above works with one change to the exporter. First, in the portal open Administration › API keys and create a token with the ingest scope. It starts with obs_ and is shown once, so put it in your secret store.

exporters:
  otlphttp/televane:
    endpoint: https://ingest.televane.com
    headers:
      Authorization: "Bearer ${env:TELEVANE_TOKEN}"

service:
  pipelines:
    traces:  { receivers: [otlp], processors: [memory_limiter, batch], exporters: [otlphttp/televane] }
    metrics: { receivers: [otlp], processors: [memory_limiter, batch], exporters: [otlphttp/televane] }
    logs:    { receivers: [otlp], processors: [memory_limiter, batch], exporters: [otlphttp/televane] }

For gRPC, use ingest.televane.com:443 with TLS. To skip the Collector, set OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.televane.com and OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer obs_YOUR_TOKEN" in each service.

A few limits to plan for. Each request may be up to 1 MiB compressed (8 MiB uncompressed) and 5,000 spans or log records, so keep send_batch_max_size below 5,000. Exponential histograms are not supported yet; use explicit-bucket histograms. Passwords, tokens, keys, card numbers and SQL literals in attributes and log bodies are masked before storage.

Traces then appear on the APM pages with throughput, error rate and p95 latency per service, a span waterfall linked to the logs of the same request, and a service map. Your own metrics can be charted on dashboards. Pricing is by the data you send, with no per-host fees, and the Free plan includes 50 GB a month (see pricing). The full reference is in Send OpenTelemetry data, and you can create an account to try it.

Frequently asked questions

Do I need the OpenTelemetry Collector?

No. SDKs can export directly to any backend that accepts OTLP. A Collector helps when you have many services, want to keep credentials out of applications, or need central filtering, redaction or tail sampling.

Which ports does the OpenTelemetry Collector use?

By convention, 4317 for OTLP over gRPC and 4318 for OTLP over HTTP. The health check extension commonly uses 13133.

Should I use the core or contrib Collector image?

Most teams use contrib, because it includes components such as tail sampling, file log collection and host metrics. You can build a smaller custom distribution later with only the components you use.

What order should Collector processors go in?

Put memory_limiter first so it can refuse data early, then processors that filter, change or sample data, and batch near the end so it batches what will actually be exported.