OpenTelemetry Collector Configuration Tutorial
OpenTelemetry Collector receives, processes, and exports telemetry data (traces, metrics, logs) from applications. This flow of receiving and forwarding telemetry data is described in more detail in OpenTelemetry Collector ingestion. Configure receivers (OTLP, Prometheus), processors (batch, sampling), and exporters (OTLP, cloud providers) using YAML. Default ports: 4317 (gRPC), 4318 (HTTP). Validate with otelcol-contrib validate --config=config.yaml.
Collector architecture
[Your App] → [Receivers] → [Processors] → [Exporters] → [Backend]
For what each stage does, see how the Collector works. What matters when writing the config is the wiring: components are declared in their own top-level blocks and activated in service.pipelines. Declaring a receiver does not start it.
One declaration can serve several pipelines — a single otlp receiver feeds traces, metrics, and logs at once — and pipelines of the same signal can run side by side with different processing.
Quick reference
The parts you will reach for on day one.
Essential components to remember
otlpreceiver for modern appsbatchprocessor for performancememory_limiterprocessor for stabilitydebugexporter for testing
Default ports
- 4317: OTLP gRPC
- 4318: OTLP HTTP
- 8888: Collector metrics
- 8889: Prometheus exporter (if configured)
Useful commands
Validate before you restart anything — a bad config makes the Collector exit rather than fall back.
# Validate config without starting the Collector
otelcol-contrib validate --config=config.yaml
# Run with debug logging
otelcol-contrib --config=config.yaml --set=service.telemetry.logs.level=debug
# Read the Collector's own metrics
curl http://localhost:8888/metrics
Basic configuration structure
Every Collector config follows this YAML structure:
# collector-config.yaml
receivers:
# How to receive data
processors:
# How to process data (optional)
exporters:
# Where to send data
service:
pipelines:
# Connect everything together
First configuration
Let's start with a minimal setup that receives OTLP data and exports it to the console:
# basic-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
exporters:
debug:
verbosity: detailed
service:
pipelines:
traces:
receivers: [otlp]
exporters: [debug]
metrics:
receivers: [otlp]
exporters: [debug]
logs:
receivers: [otlp]
exporters: [debug]
Run it:
otelcol-contrib --config=basic-config.yaml
This config:
- Accepts OTLP data on standard ports (4317 for gRPC, 4318 for HTTP)
- Prints all received data to the console
- Handles traces, metrics, and logs separately
Configuration example
Here's a more practical setup that you might use in production:
# production-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
# Scrape Prometheus metrics
prometheus:
config:
scrape_configs:
- job_name: 'my-service'
static_configs:
- targets: ['localhost:8080']
processors:
# Protect against OOM
memory_limiter:
limit_mib: 512
spike_limit_mib: 128
check_interval: 1s
# Sample traces to reduce volume
probabilistic_sampler:
sampling_percentage: 10.0
# Add resource attributes
resource:
attributes:
- key: environment
value: production
action: upsert
- key: service.version
from_attribute: app.version
action: insert
# Batch data for efficiency
batch:
timeout: 1s
send_batch_size: 1024
exporters:
# Export via OTLP to any compatible backend
otlp/backend:
endpoint: your-backend:4317
tls:
insecure: true
# Export to Prometheus
prometheus:
endpoint: "0.0.0.0:8889"
# Export to Uptrace
otlp/uptrace:
endpoint: https://api.uptrace.dev:4317
headers:
"uptrace-dsn": "${UPTRACE_DSN}"
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, probabilistic_sampler, resource, batch]
exporters: [otlp/backend, otlp/uptrace]
metrics:
receivers: [otlp, prometheus]
processors: [memory_limiter, resource, batch]
exporters: [prometheus, otlp/uptrace]
logs:
receivers: [otlp]
processors: [memory_limiter, resource, batch]
exporters: [otlp/uptrace]
Configuration patterns
Three patterns cover most of what teams actually change after the first config works.
Pattern 1: multi-environment setup
Use different configs for dev/staging/prod:
# Use environment variables for flexibility
exporters:
otlp:
endpoint: ${env:OTLP_ENDPOINT}
processors:
probabilistic_sampler:
sampling_percentage: ${env:SAMPLING_RATE:-100.0} # Default 100% if not set
Pattern 2: data enrichment
Add context to your telemetry:
processors:
resource:
attributes:
- key: k8s.cluster.name
value: ${env:K8S_CLUSTER_NAME}
action: upsert
- key: deployment.environment
value: ${env:ENVIRONMENT}
action: upsert
transform:
trace_statements:
- context: span
statements:
- set(attributes["custom.field"], "processed-by-collector")
Pattern 3: data filtering
Remove unwanted data using OTTL conditions:
processors:
filter:
error_mode: ignore
traces:
span:
- 'attributes["http.route"] == "/health"'
- 'name == "GET /metrics"'
metrics:
metric:
- 'name == "unwanted_metric"'
Receiver
Receivers are how data gets in. Most pipelines need exactly one — otlp — plus another only when you are collecting from something that cannot send OTLP itself.
OTLP receiver (most common)
Accepts data from any OpenTelemetry SDK. Bind to 0.0.0.0 inside containers, or the Collector will only accept connections from itself.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
# Optional: TLS configuration
tls:
cert_file: /path/to/cert.pem
key_file: /path/to/key.pem
http:
endpoint: 0.0.0.0:4318
cors:
allowed_origins: ["*"] # Be more restrictive in production
Prometheus receiver
Scrapes existing /metrics endpoints, so services already exposing Prometheus metrics need no code changes. See Prometheus integration for the full setup, and host metrics for CPU, memory, and disk.
receivers:
prometheus:
config:
scrape_configs:
- job_name: 'my-app'
scrape_interval: 30s
static_configs:
- targets: ['app:8080']
metrics_path: /metrics
Filelog receiver (for logs)
Tails log files and parses them into OTLP records — the usual route when your language's logs SDK is not stable yet.
receivers:
filelog:
include: [/var/log/myapp/*.log]
operators:
- type: json_parser
timestamp:
parse_from: attributes.timestamp
layout: '%Y-%m-%d %H:%M:%S'
Processor
Processors run in the order you list them in the pipeline. Two belong in every production config: memory_limiter first, batch last.
Batch processor (essential for performance)
Groups records before export. Without it, every span becomes its own request.
processors:
batch:
timeout: 1s # How long to wait before sending
send_batch_size: 512 # Send when this many items collected
send_batch_max_size: 1024 # Never exceed this size
Sampling processors
probabilistic_sampler drops a fixed share cheaply. tail_sampling waits for the whole trace and can keep exactly the errors and slow requests — at the cost of buffering every span in memory until it decides. See sampling for the trade-off and the replica constraint.
processors:
# Sample 10% of traces
probabilistic_sampler:
sampling_percentage: 10.0
# More sophisticated sampling
tail_sampling:
decision_wait: 10s
policies:
- name: error-traces
type: status_code
status_code: {status_codes: [ERROR]}
- name: slow-traces
type: latency
latency: {threshold_ms: 1000}
- name: random-sample
type: probabilistic
probabilistic: {sampling_percentage: 1.0}
Exporter
Exporters send processed data to one or more backends. For complete exporter configuration including all available backends, authentication, and production patterns, see OpenTelemetry Collector Exporters.
Cloud provider exporters
For sending to a managed backend that does not accept OTLP directly.
exporters:
# Google Cloud
googlecloud:
project: my-gcp-project
# AWS X-Ray
awsxray:
region: us-west-2
# Azure Monitor
azuremonitor:
connection_string: ${env:APPLICATIONINSIGHTS_CONNECTION_STRING}
File exporter (for debugging)
Writes raw telemetry to disk, which is the fastest way to confirm what actually reached the Collector.
exporters:
file:
path: /tmp/otel-data.json
rotation:
max_megabytes: 100
max_days: 7
max_backups: 3
Environment variables and secrets
Keep sensitive data out of your config files:
# In your config
exporters:
otlp/backend:
endpoint: ${BACKEND_ENDPOINT}
headers:
authorization: "Bearer ${API_TOKEN}"
# In your environment
export BACKEND_ENDPOINT="https://api.example.com"
export API_TOKEN="your-secret-token"
Docker deployment
Here's a complete Docker setup:
# Dockerfile
FROM otel/opentelemetry-collector-contrib:latest
COPY collector-config.yaml /etc/otelcol-contrib/config.yaml
EXPOSE 4317 4318 8889
# docker-compose.yml
services:
otel-collector:
build: .
ports:
- "4317:4317" # OTLP gRPC
- "4318:4318" # OTLP HTTP
- "8889:8889" # Prometheus metrics
environment:
- OTLP_ENDPOINT=otelcol-gateway:4317
volumes:
- ./logs:/var/log
This setup works well for local development and simple deployments. In production environments, the Collector is typically deployed using orchestration platforms like Kubernetes, where you can manage scaling, configuration, and service discovery more effectively. For example, you can monitor Kubernetes with OpenTelemetry Collector to collect telemetry from your cluster.
Testing your configuration
Three checks, in order: does the config parse, is the Collector running, does data get through.
1. Validate syntax
otelcol-contrib validate --config=your-config.yaml
2. Check what's running
The Collector reports on itself. otelcol_receiver_accepted_spans and otelcol_exporter_sent_spans tell you whether data is entering and leaving.
# The collector exposes metrics about itself
curl http://localhost:8888/metrics
3. Send test data
A single span over OTLP/HTTP, no SDK involved — it isolates the Collector from your application.
# Send a test trace using curl
curl -X POST http://localhost:4318/v1/traces \
-H "Content-Type: application/json" \
-d '{
"resourceSpans": [{
"resource": {"attributes": [{"key": "service.name", "value": {"stringValue": "test-service"}}]},
"scopeSpans": [{
"spans": [{
"traceId": "5b8aa5a2d2c872e8321cf37308d69df2",
"spanId": "051581bf3cb55c13",
"name": "test-span",
"kind": "SPAN_KIND_CLIENT",
"startTimeUnixNano": "1640995200000000000",
"endTimeUnixNano": "1640995200100000000"
}]
}]
}]
}'
Common troubleshooting
For the full pipeline-by-pipeline procedure, see OpenTelemetry troubleshooting.
Configuration not loading
- Check YAML syntax (indentation matters!)
- Verify file permissions
- Look for typos in component names
Data not flowing
- Check that receivers are listening on the right ports
- Verify pipeline connections (receivers → processors → exporters)
- Look at Collector logs:
otelcol-contrib --config=config.yaml --set=service.telemetry.logs.level=debug
Performance issues
- Add batch processor if missing
- Reduce sampling rates
- Check memory_limiter processor configuration
Memory usage growing
Add memory_limiter as the first processor in every pipeline so it can reject data before the rest of the chain allocates for it.
processors:
memory_limiter:
limit_mib: 512
spike_limit_mib: 128
Advanced use cases
Multi-pipeline setup
Pipelines of the same type can run side by side with different processing. A common use is keeping every error trace while sampling the rest.
service:
pipelines:
# High-priority traces (errors) with no sampling
traces/errors:
receivers: [otlp]
processors: [filter/errors, batch]
exporters: [otlp/backend]
# Normal traces with sampling
traces/sampled:
receivers: [otlp]
processors: [filter/normal, probabilistic_sampler, batch]
exporters: [otlp/backend]
Data routing by attributes
The routing processor is deprecated. Use the routing connector, which routes to pipelines rather than straight to exporters — so you can still run processors after the routing decision. It is declared under connectors: and appears as an exporter in the incoming pipeline and as a receiver in each destination.
connectors:
routing:
default_pipelines: [traces/other]
table:
- context: resource
condition: attributes["service.name"] == "frontend"
pipelines: [traces/frontend]
service:
pipelines:
traces/in:
receivers: [otlp]
processors: [batch]
exporters: [routing]
traces/frontend:
receivers: [routing]
exporters: [otlp/frontend]
traces/other:
receivers: [routing]
exporters: [otlp/backend]
What's next?
- OpenTelemetry Collector — installation and the pipeline model
- Collector exporters — every backend, retry, queue, and TLS setting
- Prometheus integration — receive from and write to Prometheus
- Host metrics receiver — CPU, memory, disk, and network
- OpenTelemetry sampling — where tail sampling runs
- OpenTelemetry troubleshooting — when nothing arrives