Dash0 Raises $35 Million Series A to Build the First AI-Native Observability Platform

Last updated: December 1, 2025

OpenTelemetry Host Metrics: The Node Exporter Alternative

When it comes to infrastructure monitoring, the Host Metrics Receiver (hostmetrics) is the cornerstone component in an OpenTelemetry Collector pipeline.

It serves as a replacement for traditional agents (like Prometheus Node Exporter), and allows you to gather critical system metrics like CPU, memory, disk, and network usage, directly from the host where the Collector is running.

Because it needs direct access to the underlying system, this receiver is specifically designed to be used when the Collector is deployed as an Agent (a DaemonSet or a local system service), rather than as a central Gateway.

In this guide, you'll learn how to configure it effectively for real-world production environments.

Quick start: collecting host metrics

To see the receiver in action, let's spin up a Docker Compose setup that uses the Collector to scrape your host's vitals and send that data directly to Prometheus.

You'll need three files. First, create a docker-compose.yaml to orchestrate the services:

yaml
12345678910111213141516171819202122232425262728293031
# docker-compose.yaml
services:
otelcol:
image: otel/opentelemetry-collector-contrib:0.140.0
container_name: otelcol
volumes:
- ./otelcol.yaml:/etc/otelcol-contrib/config.yaml
- /:/hostfs:ro
restart: unless-stopped
depends_on:
- prometheus
prometheus:
image: prom/prometheus:v3.7.3
container_name: prometheus
restart: unless-stopped
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=15d
- --web.enable-lifecycle
# Enable Prometheus to accept OTLP writes directly
- --web.enable-otlp-receiver
ports:
- 9090:9090
volumes:
prometheus_data:

There are two critical details here:

  1. You must mount the host's root directory (/) to /hostfs inside the container. Without this, the Collector would only see the statistics of its own tiny container, not your actual server.

  2. The --web.enable-otlp-receiver flag turns Prometheus into an OTLP endpoint, allowing the Collector to push data to it via standard HTTP.

Next, create the otelcol.yaml configuration file:

yaml
1234567891011121314151617181920212223242526272829303132
# otelcol.yaml
receivers:
hostmetrics:
# Tell the scrapers to look at the mounted volume, not the container's /
root_path: /hostfs
collection_interval: 1m # Default is 1m, adjust based on resolution needs
scrapers:
cpu:
memory:
load:
network:
disk:
filesystem:
processors:
batch:
exporters:
debug:
verbosity: detailed
otlphttp/prometheus:
# Push metrics to Prometheus's OTLP receiver
metrics_endpoint: http://prometheus:9090/api/v1/otlp/v1/metrics
tls:
insecure: true
service:
pipelines:
metrics:
receivers: [hostmetrics]
processors: [batch]
exporters: [otlphttp/prometheus]

The key setting here is root_path: /hostfs which explicitly tells the hostmetrics receiver to look at the volume we mounted in the previous step, rather than the container's default filesystem.

Finally, create a minimal prometheus.yml so Prometheus can start:

yaml
123
# prometheus.yml
global:
scrape_interval: 10s

Run the command below to launch the services:

bash
1
docker compose up -d

After a minute, navigate to http://localhost:9090 in your browser. You should see metrics like system_cpu_time_seconds_total and system_memory_usage_bytes flowing in from your host.

Prometheus screenshot showing metric

Understanding hostmetrics scrapers

The hostmetrics receiver acts as a scheduler, but the heavy lifting is done by scrapers. These are modular plugins that you enable individually in the scrapers section of your configuration.

If you are coming from the Prometheus ecosystem, you can think of these scrapers as the equivalent of Node Exporter collectors.

They generate metrics that generally follow OpenTelemetry Semantic Conventions, which may look slightly different from what you are used to seeing in Prometheus.

Here's how the most common scrapers map to the Node Exporter equivalents you might already be familiar with:

OTel ScraperTypical Metric NameNode Exporter Equivalent
cpusystem.cpu.*node_cpu_*
memorysystem.memory.*node_memory_*
loadsystem.cpu.load_average.1mnode_load1
filesystemsystem.filesystem.*node_filesystem_*
disksystem.disk.*node_disk_*
networksystem.network.*node_network_*

While this covers the most commonly used scrapers, there are a few details worth noting:

  • The default memory scraper only looks at physical RAM usage. If you're trying to debug performance issues caused by memory pressure, you need to explicitly enable the paging scraper to see swap usage and page faults.

  • Be very careful with the difference between processes (plural) and process (singular) scrapers:

    • processes is cheap; it simply counts the total number of running or blocked threads on the host.
    • process is heavy; it scrapes detailed CPU and memory metrics for every single executable running on the system. Turning this on without strict filtering through an allowlist is the fastest way to explode your metric cardinality.
  • The filesystem and disk scrapers are easy to confuse. The former measures free space and tells you when the disk is full, while the latter measures IOPS and throughput to help you debug slow I/O performance. You usually need both.

Configuring scrapers

Enabling a scraper isn't always a "set it and forget it" action. In production, you often need to tune exactly what they scrape to avoid flooding your backend with useless data.

The most basic thing is controlling the exact metric data points that are generated. Every scraper comes with a set of default metrics (which are on by default) and optional metrics (which are off by default).

You can toggle these using the metrics block within the scraper configuration:

yaml
123456789
# otelcol.yaml
receivers:
hostmetrics:
scrapers:
filesystem:
metrics:
# Enable optional metrics that are off by default
system.filesystem.utilization:
enabled: true

Reducing noise with filters

Most resource-heavy scrapers (disk, filesystem, network, process) support using include and exclude rules to restrict data collection to specific devices or filter out noisy interfaces. These rules can be applied using exact string matches (strict) or regular expressions (regexp).

For example, to prevent the Network scraper from generating data for local loopback or Docker bridge interfaces, you can exclude them by name:

yaml
12345678
# otelcol.yaml
receivers:
hostmetrics:
scrapers:
network:
exclude:
interfaces: ["lo", "docker0"]
match_type: strict # Options: strict or regexp

Similarly, the Filesystem scraper can be noisy if it tracks every specialized system mount. You likely want to filter out tmpfs or overlay filesystems:

yaml
12345678
# otelcol.yaml
filesystem:
exclude_fs_types:
fs_types: ["tmpfs", "autofs", "overlay"]
match_type: strict
exclude_mount_points:
mount_points: ["/var/lib/docker/*"]
match_type: regexp

Optimizing metric collection intervals

Not all metrics require the same granularity. You typically need high-resolution polling for volatile resources like CPU and memory to catch transient spikes, while slow-moving metrics like filesystem usage can be checked much less frequently to save resources.

To achieve this, you can define named instances of the receiver to configure them with independent collection_interval settings and target different sets of scrapers:

yaml
1234567891011121314151617181920
receivers:
# High-resolution scraping for volatile metrics
hostmetrics/fast:
collection_interval: 10s
scrapers:
cpu:
memory:
# Lower-resolution scraping for stable metrics
hostmetrics/slow:
collection_interval: 1m
scrapers:
filesystem:
disk:
service:
pipelines:
metrics:
# Both instances feed into the same pipeline
receivers: [hostmetrics/fast, hostmetrics/slow]

Note that if you're running multiple instances of the hostmetrics receiver in the same Collector, they must all share the same root_path.

Silencing permission errors

The process scraper is particularly sensitive to permissions. It attempts to read details for every process, including those owned by root or other users. In a restricted container environment, this may generate a flood of "permission denied" logs.

You can silence these errors specifically without losing visibility into the processes you can access:

yaml
123
process:
mute_process_user_error: true # Mute "user does not exist" errors
mute_process_io_error: true # Mute "permission denied" on I/O stats

One of the biggest "gotchas" with the hostmetrics receiver is that it generates "naked" metrics. By default, it does not attach critical resource metadata—like host.name, service.namespace, or cloud.region—to the telemetry it produces.

Without these attributes, your backend will receive a stream of CPU and memory numbers, but you will have no easy way to distinguish which specific server or environment they belong to.

To solve this, you need to enrich the data before it leaves the collector.

You can manually inject attributes by setting the standard OTEL_RESOURCE_ATTRIBUTES environment variable on your Collector process:

bash
1
export OTEL_RESOURCE_ATTRIBUTES="service.name=my-payment-service,host.name=prod-server-01"

In production, you should use the resourcedetection processor. It automatically queries the underlying platform APIs to discover and attach the correct metadata.

Refer to our resource processor guide to learn how to manage these attributes effectively.

Final thoughts

The Host Metrics receiver is a workhorse of the OpenTelemetry ecosystem that provides the essential baseline visibility that every service needs.

But raw metrics alone only tell half the story. The true value comes when you can instantly correlate a spike in CPU usage with the specific service, trace, or database query causing it.

At Dash0, we built our platform around this exact concept. Because we're OpenTelemetry-native, we automatically connect your infrastructure metrics to your application traces and logs, giving you a unified view of your system's health from the moment data arrives.

So stop guessing if it's the code or the infrastructure. Try Dash0 today and see the full picture.

Authors
Ayooluwa Isaiah
Ayooluwa Isaiah