Configure OpenTelemetry Collector to gather metrics from distributed services and export them to Prometheus for comprehensive observability monitoring. This integration provides unified metrics collection across your microservices architecture.
Prerequisites
- Root or sudo access
- Running Prometheus instance
- Basic understanding of YAML configuration
- Network access to download packages
What this solves
OpenTelemetry metrics collection with Prometheus integration provides unified observability for distributed systems by collecting metrics from multiple services and exporting them to a centralized Prometheus instance. This setup eliminates the need for multiple monitoring agents and provides standardized metrics collection across your microservices architecture.
Prerequisites
You need a running Prometheus instance and basic understanding of YAML configuration. If you don't have Prometheus set up yet, follow our Prometheus installation guide.
Step-by-step installation
Install OpenTelemetry Collector
Download and install the OpenTelemetry Collector binary from the official GitHub releases.
sudo apt update
wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.91.0/otelcol_0.91.0_linux_amd64.deb
sudo dpkg -i otelcol_0.91.0_linux_amd64.deb
Create OpenTelemetry Collector configuration
Configure the collector to receive metrics from various sources and export them to Prometheus. This configuration includes OTLP receivers, processors, and Prometheus exporters.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
prometheus:
config:
scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['localhost:8888']
hostmetrics:
collection_interval: 30s
scrapers:
cpu:
disk:
filesystem:
memory:
network:
process:
processors:
batch:
timeout: 1s
send_batch_size: 1024
memory_limiter:
limit_mib: 256
spike_limit_mib: 64
resourcedetection:
detectors: [env, system, docker]
timeout: 5s
override: false
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
namespace: "otel"
const_labels:
environment: "production"
enable_open_metrics: true
logging:
loglevel: info
service:
extensions: [health_check, pprof, zpages]
pipelines:
metrics:
receivers: [otlp, prometheus, hostmetrics]
processors: [memory_limiter, resourcedetection, batch]
exporters: [prometheus, logging]
telemetry:
logs:
level: "info"
metrics:
address: 0.0.0.0:8888
Configure Prometheus to scrape OpenTelemetry metrics
Add the OpenTelemetry Collector as a scrape target in your Prometheus configuration to collect the exported metrics.
global:
scrape_interval: 30s
evaluation_interval: 30s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'otel-collector'
static_configs:
- targets: ['localhost:8889']
scrape_interval: 15s
metrics_path: '/metrics'
- job_name: 'otel-collector-internal'
static_configs:
- targets: ['localhost:8888']
scrape_interval: 30s
metrics_path: '/metrics'
Create systemd service for OpenTelemetry Collector
Set up a systemd service to manage the OpenTelemetry Collector process with automatic restarts and proper logging.
[Unit]
Description=OpenTelemetry Collector
Documentation=https://opentelemetry.io/docs/collector/
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
User=otel
Group=otel
ExecStart=/usr/bin/otelcol --config=/etc/otelcol/config.yaml
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=otel-collector
KillMode=mixed
KillSignal=SIGTERM
[Install]
WantedBy=multi-user.target
Create dedicated user for OpenTelemetry Collector
Create a system user with minimal privileges to run the OpenTelemetry Collector service securely.
sudo useradd --system --no-create-home --shell /bin/false otel
sudo chown -R otel:otel /etc/otelcol
sudo chmod 644 /etc/otelcol/config.yaml
Configure log rotation
Set up log rotation to prevent log files from consuming excessive disk space over time.
/var/log/otel-collector/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 644 otel otel
postrotate
systemctl reload otel-collector > /dev/null 2>&1 || true
endscript
}
Start and enable services
Enable both OpenTelemetry Collector and Prometheus services to start automatically on boot and start them immediately.
sudo systemctl daemon-reload
sudo systemctl enable --now otel-collector
sudo systemctl restart prometheus
sudo systemctl enable prometheus
Configure firewall rules
Open the necessary ports for OpenTelemetry Collector receivers and Prometheus metrics endpoint.
sudo ufw allow 4317/tcp comment 'OpenTelemetry GRPC'
sudo ufw allow 4318/tcp comment 'OpenTelemetry HTTP'
sudo ufw allow 8889/tcp comment 'OpenTelemetry Prometheus metrics'
sudo ufw reload
Configure application instrumentation
Set up Node.js application metrics
Configure a Node.js application to send metrics to the OpenTelemetry Collector using automatic instrumentation.
npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
const metricExporter = new OTLPMetricExporter({
url: 'http://localhost:4318/v1/metrics',
});
const sdk = new NodeSDK({
instrumentations: [getNodeAutoInstrumentations()],
metricReader: new PeriodicExportingMetricReader({
exporter: metricExporter,
exportIntervalMillis: 5000,
}),
serviceName: 'example-service',
serviceVersion: '1.0.0',
});
sdk.start();
Configure custom metrics collection
Add custom business metrics to track application-specific KPIs alongside infrastructure metrics.
const { metrics } = require('@opentelemetry/api');
const meter = metrics.getMeter('example-service', '1.0.0');
// Counter for request count
const requestCounter = meter.createCounter('http_requests_total', {
description: 'Total number of HTTP requests',
});
// Histogram for request duration
const requestDuration = meter.createHistogram('http_request_duration_ms', {
description: 'Duration of HTTP requests in milliseconds',
});
// Gauge for active connections
const activeConnections = meter.createUpDownCounter('active_connections', {
description: 'Number of active connections',
});
// Example usage in Express middleware
function metricsMiddleware(req, res, next) {
const startTime = Date.now();
requestCounter.add(1, {
method: req.method,
route: req.route?.path || 'unknown',
});
activeConnections.add(1);
res.on('finish', () => {
const duration = Date.now() - startTime;
requestDuration.record(duration, {
method: req.method,
status_code: res.statusCode.toString(),
});
activeConnections.add(-1);
});
next();
}
module.exports = { metricsMiddleware };
Advanced configuration
Configure metrics sampling and filtering
Implement sampling and filtering to reduce metric cardinality and improve performance in high-traffic environments.
processors:
filter:
metrics:
exclude:
match_type: regexp
metric_names:
- ".debug."
- ".test."
transform:
metric_statements:
- context: metric
statements:
- set(description, "Processed by OpenTelemetry") where name == "http_requests_total"
- context: datapoint
statements:
- limit(attributes, 10, [])
- delete_key(attributes, "sensitive_data")
groupbyattrs:
keys:
- service.name
- service.version
- deployment.environment
Set up high availability configuration
Configure multiple OpenTelemetry Collector instances with load balancing for production resilience.
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
loadbalancing:
routing_key: "service"
resolver:
static:
hostnames:
- otel-collector-1.example.com:4317
- otel-collector-2.example.com:4317
- otel-collector-3.example.com:4317
batch:
timeout: 1s
send_batch_size: 1024
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
namespace: "otel"
enable_open_metrics: true
otlp:
endpoint: http://prometheus-remote-write.example.com:9090/api/v1/write
headers:
X-Prometheus-Remote-Write-Version: "0.1.0"
service:
pipelines:
metrics:
receivers: [otlp]
processors: [loadbalancing, batch]
exporters: [prometheus, otlp]
Verify your setup
Check that all services are running correctly and metrics are being collected and exported properly.
sudo systemctl status otel-collector
sudo systemctl status prometheus
curl http://localhost:8889/metrics | head -20
curl http://localhost:9090/api/v1/targets
journal -u otel-collector -n 50
Verify metrics are appearing in Prometheus by accessing the Prometheus web UI at http://your-server:9090 and searching for metrics with the otel_ prefix.
Integration with existing monitoring stack
If you have an existing Prometheus monitoring setup, you can integrate OpenTelemetry metrics with federation or remote write. For comprehensive monitoring of specific services, check our guides on Elasticsearch monitoring and Redis monitoring.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Collector fails to start | YAML syntax error in configuration | otelcol --config=/etc/otelcol/config.yaml --dry-run |
| Metrics not appearing in Prometheus | Prometheus not scraping collector endpoint | Check http://localhost:9090/targets for scrape status |
| High memory usage | Memory limiter not configured properly | Lower limit_mib in memory_limiter processor |
| Missing host metrics | Collector user lacks permissions | sudo usermod -aG adm,systemd-journal otel |
| Connection refused errors | Firewall blocking required ports | Verify firewall rules and service bindings |
Next steps
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration variables
OTEL_VERSION="0.91.0"
OTEL_USER="otel"
OTEL_CONFIG_DIR="/etc/otelcol"
OTEL_CONFIG_FILE="${OTEL_CONFIG_DIR}/config.yaml"
PROMETHEUS_CONFIG_FILE="/etc/prometheus/prometheus.yml"
# Progress counter
STEP=1
TOTAL_STEPS=8
# Cleanup function for rollback on failure
cleanup() {
echo -e "${RED}[ERROR] Installation failed. Rolling back...${NC}"
systemctl stop otel-collector 2>/dev/null || true
systemctl disable otel-collector 2>/dev/null || true
rm -f /etc/systemd/system/otel-collector.service
rm -rf "${OTEL_CONFIG_DIR}"
userdel -r "${OTEL_USER}" 2>/dev/null || true
echo -e "${RED}[ERROR] Rollback completed${NC}"
exit 1
}
trap cleanup ERR
print_usage() {
echo "Usage: $0 [OPTIONS]"
echo "Options:"
echo " --prometheus-config PATH Path to Prometheus config file (default: ${PROMETHEUS_CONFIG_FILE})"
echo " --help Show this help message"
}
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--prometheus-config)
PROMETHEUS_CONFIG_FILE="$2"
shift 2
;;
--help)
print_usage
exit 0
;;
*)
echo -e "${RED}Unknown option: $1${NC}"
print_usage
exit 1
;;
esac
done
echo -e "${GREEN}OpenTelemetry Collector Installation Script${NC}"
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Detect distribution and set package manager
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
PACKAGE_EXT="deb"
INSTALL_CMD="dpkg -i"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
PACKAGE_EXT="rpm"
INSTALL_CMD="rpm -ivh"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
PACKAGE_EXT="rpm"
INSTALL_CMD="rpm -ivh"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect distribution${NC}"
exit 1
fi
echo -e "${GREEN}[${STEP}/${TOTAL_STEPS}] Updating package repositories...${NC}"
$PKG_UPDATE
((STEP++))
echo -e "${GREEN}[${STEP}/${TOTAL_STEPS}] Installing required packages...${NC}"
$PKG_INSTALL wget curl
((STEP++))
echo -e "${GREEN}[${STEP}/${TOTAL_STEPS}] Downloading and installing OpenTelemetry Collector...${NC}"
cd /tmp
if [[ "$PACKAGE_EXT" == "deb" ]]; then
wget "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_VERSION}/otelcol_${OTEL_VERSION}_linux_amd64.deb"
$INSTALL_CMD "otelcol_${OTEL_VERSION}_linux_amd64.deb"
else
wget "https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v${OTEL_VERSION}/otelcol-${OTEL_VERSION}-1.x86_64.rpm"
$INSTALL_CMD "otelcol-${OTEL_VERSION}-1.x86_64.rpm"
fi
((STEP++))
echo -e "${GREEN}[${STEP}/${TOTAL_STEPS}] Creating OpenTelemetry user and directories...${NC}"
useradd --system --no-create-home --shell /bin/false "${OTEL_USER}" || true
mkdir -p "${OTEL_CONFIG_DIR}"
chown -R "${OTEL_USER}:${OTEL_USER}" "${OTEL_CONFIG_DIR}"
chmod 755 "${OTEL_CONFIG_DIR}"
((STEP++))
echo -e "${GREEN}[${STEP}/${TOTAL_STEPS}] Creating OpenTelemetry Collector configuration...${NC}"
cat > "${OTEL_CONFIG_FILE}" << 'EOF'
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
prometheus:
config:
scrape_configs:
- job_name: 'otel-collector'
static_configs:
- targets: ['localhost:8888']
hostmetrics:
collection_interval: 30s
scrapers:
cpu:
disk:
filesystem:
memory:
network:
process:
processors:
batch:
timeout: 1s
send_batch_size: 1024
memory_limiter:
limit_mib: 256
spike_limit_mib: 64
resourcedetection:
detectors: [env, system, docker]
timeout: 5s
override: false
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
namespace: "otel"
const_labels:
environment: "production"
enable_open_metrics: true
logging:
loglevel: info
service:
extensions: [health_check, pprof, zpages]
pipelines:
metrics:
receivers: [otlp, prometheus, hostmetrics]
processors: [memory_limiter, resourcedetection, batch]
exporters: [prometheus, logging]
telemetry:
logs:
level: "info"
metrics:
address: 0.0.0.0:8888
EOF
chown "${OTEL_USER}:${OTEL_USER}" "${OTEL_CONFIG_FILE}"
chmod 644 "${OTEL_CONFIG_FILE}"
((STEP++))
echo -e "${GREEN}[${STEP}/${TOTAL_STEPS}] Creating systemd service...${NC}"
cat > /etc/systemd/system/otel-collector.service << EOF
[Unit]
Description=OpenTelemetry Collector
Documentation=https://opentelemetry.io/docs/collector/
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
User=${OTEL_USER}
Group=${OTEL_USER}
ExecStart=/usr/bin/otelcol --config=${OTEL_CONFIG_FILE}
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=otel-collector
KillMode=mixed
KillSignal=SIGTERM
[Install]
WantedBy=multi-user.target
EOF
chmod 644 /etc/systemd/system/otel-collector.service
systemctl daemon-reload
systemctl enable otel-collector
((STEP++))
echo -e "${GREEN}[${STEP}/${TOTAL_STEPS}] Configuring firewall...${NC}"
# Configure firewall based on distribution
if command -v firewall-cmd &> /dev/null; then
firewall-cmd --permanent --add-port=4317/tcp
firewall-cmd --permanent --add-port=4318/tcp
firewall-cmd --permanent --add-port=8888/tcp
firewall-cmd --permanent --add-port=8889/tcp
firewall-cmd --reload
elif command -v ufw &> /dev/null; then
ufw allow 4317/tcp
ufw allow 4318/tcp
ufw allow 8888/tcp
ufw allow 8889/tcp
fi
((STEP++))
echo -e "${GREEN}[${STEP}/${TOTAL_STEPS}] Starting OpenTelemetry Collector service...${NC}"
systemctl start otel-collector
((STEP++))
echo -e "${GREEN}Installation completed successfully!${NC}"
echo ""
echo -e "${YELLOW}Verification:${NC}"
# Verify service status
if systemctl is-active --quiet otel-collector; then
echo -e "✅ OpenTelemetry Collector service is ${GREEN}running${NC}"
else
echo -e "❌ OpenTelemetry Collector service is ${RED}not running${NC}"
fi
# Verify ports
for port in 4317 4318 8888 8889; do
if ss -tlnp | grep -q ":${port} "; then
echo -e "✅ Port ${port} is ${GREEN}listening${NC}"
else
echo -e "❌ Port ${port} is ${RED}not listening${NC}"
fi
done
echo ""
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Update your Prometheus configuration to scrape from localhost:8889"
echo "2. Add the following to your prometheus.yml:"
echo " - job_name: 'otel-collector'"
echo " static_configs:"
echo " - targets: ['localhost:8889']"
echo "3. Configure your applications to send metrics to:"
echo " - OTLP gRPC: localhost:4317"
echo " - OTLP HTTP: localhost:4318"
echo ""
echo -e "${GREEN}OpenTelemetry Collector is ready for use!${NC}"
Review the script before running. Execute with: bash install.sh