SNMP
The OpenTelemetry Collector's snmpreceiver polls SNMP-speaking
devices over UDP, converts enterprise and standard MIB OIDs to named
metrics, and exports OTLP to base14 Scout. Use it for network gear
(routers, switches, firewalls), power infrastructure (UPSes, PDUs),
printers, and any host running net-snmp. This guide configures
three representative device profiles — a Linux host, a Cisco-style
router, and an APC-style UPS — and ships the metrics upstream.
The same pattern generalises to any SNMPv1/v2c/v3-capable device.
Because snmpreceiver is poll-only, it is particularly well suited
to monitoring infrastructure that cannot run agents itself — which
makes SNMP a natural bridge into broader IoT and OT telemetry work.
Prerequisites
| Requirement | Minimum | Recommended |
|---|---|---|
| SNMP version | v1 | v2c or v3 |
| OTel Collector Contrib | 0.90.0 | 0.153.0 |
| Target devices | SNMP-enabled | SNMPv2c/v3 with community or user configured |
| base14 Scout | Any | - |
Before starting:
- Devices must be reachable over UDP/161 from the Collector host
- A read-only community string (v1/v2c) or SNMPv3 user with appropriate auth/priv keys
- OTel Collector installed — see Docker Compose Setup
What You'll Monitor
snmpreceiver has no fixed metric set. Each metric below exists
because the config maps an OID — scalar or column — to an OTel dotted
name with an explicit unit and type, so the emitted set IS the config;
you choose the names when you map each OID. The tiers are organised by
how you use the metrics across three device classes: [net] for the
router, [ups] for the UPS, [host] for the Linux host.
Four things shape how this surface reads, regardless of device:
- There is no
upseries and no health metric. Liveness is the receiver scraping the device successfully — the device answering its OIDs. A sustained scrape error in the Collector log is the device-unreachable signal. - Two metric types.
network.ioandnetwork.errorsare monotonic cumulative sums (interface octet/error counters, each carrying adirectionattribute); everything else is a gauge. - One endpoint per receiver instance. Use one
snmp/<device>block per device (or per device class sharing credentials). Each pipeline stamps a distinctservice.name, so every device appears as its own service in Scout. Column OIDs fan out to one resource per row —network.interface.namefromifName,cpu.indexfrom thehrProcessorTable. - Static labels come from a processor, not the receiver.
device.kind(compute / network / power),device.manufacturer,device.model.identifier, andsite.idare added by aresourceprocessor —snmpreceivercan only attach values it actually fetches over SNMP.
Several MIBs store fixed-point values as scaled integers:
system.cpu.load_average.* is laLoadInt (×100),
ups.battery.runtime_remaining is TimeTicks (centiseconds, not
seconds), and some PowerNet frequencies use units of 0.1 Hz. Rescale
these in the backend or a transform processor; do not assume base
units.
Core - is the link, power, or host healthy
The per-device-class headline pair: link state plus throughput for the router, output state plus battery runway for the UPS, load plus available memory for the host.
| Metric | Type | Class | What it tells you |
|---|---|---|---|
network.interface.oper_status | gauge | [net] | Operational link state (1=up, 2=down, …) — the outage signal. |
network.io | sum | [net] | Per-interface bytes in/out (ifHCOctets) — headline throughput. |
ups.output.status | gauge | [ups] | 2=onLine, 3=onBattery, … — the utility-power-lost signal. |
ups.battery.capacity | gauge | [ups] | Remaining battery capacity (%) — the runway. |
system.cpu.load_average.1m | gauge | [host] | 1-minute load average ×100 — headline host load. |
system.memory.available | gauge | [host] | Available real memory (KiB) — headline host memory. |
Operational - what to alert on
The error counters, status/replace enums, voltage and load readings,
and the saturation denominators (network.interface.speed,
system.memory.total) the Core numbers are read against.
| Metric | Type | Class | What it tells you |
|---|---|---|---|
network.errors | sum | [net] | Interface in/out errors — rising = a link or cabling problem. |
network.interface.admin_status | gauge | [net] | Administrative state (1=up, 2=down) — compare with oper_status. |
network.interface.speed | gauge | [net] | Nominal speed (Mbit/s) — denominator for saturation. |
ups.battery.status | gauge | [ups] | 1=unknown, 2=normal, 3=low (replace is the separate ups.battery.replace_indicator). |
ups.battery.replace_indicator | gauge | [ups] | 2=battery needs replacing. |
ups.battery.runtime_remaining | gauge | [ups] | Estimated runtime (centiseconds, TimeTicks). |
ups.input.voltage | gauge | [ups] | Input line voltage (V) — utility quality. |
ups.output.load | gauge | [ups] | Output load (% of rated capacity) — overload risk. |
system.cpu.utilization | gauge | [host] | Per-CPU utilization (%, per-core fan-out). |
system.cpu.load_average.5m | gauge | [host] | 5-minute load average ×100. |
system.cpu.load_average.15m | gauge | [host] | 15-minute load average ×100. |
system.memory.total | gauge | [host] | Total real memory (KiB) — denominator for memory pressure. |
system.processes.count | gauge | [host] | Running process count. |
Diagnostic - for investigation and tuning
Inventory and second-order context. Reach for these during an incident or a capacity review.
| Metric | Type | Class | What it tells you |
|---|---|---|---|
system.network.interfaces.count | gauge | [net] | ifNumber — interface inventory. |
system.users.count | gauge | [host] | Active user sessions. |
system.memory.cached | gauge | [host] | Cached memory (KiB). |
system.memory.buffered | gauge | [host] | Buffered memory (KiB). |
ups.battery.temperature | gauge | [ups] | Battery temperature (Cel). |
ups.input.frequency | gauge | [ups] | Input line frequency (Hz). |
ups.output.voltage | gauge | [ups] | Output voltage (V). |
ups.output.current | gauge | [ups] | Output current (A). |
Full receiver reference: OTel SNMP Receiver.
The runnable example with a built-in SNMP simulator lives at
base14/examples — components/snmp-telemetry.
It ships three simulated devices (communities linux-host,
cisco-router, apc-ups) on udp://snmpsim:1161, so the config
below works unchanged against the simulator if you swap the real
endpoints for that address.
Key Alerts to Configure
The state reads below — oper_status == 2, output.status == 3,
battery.status == 3, replace_indicator == 2 — are MIB-defined enum
values, not invented thresholds. Everything else is relative to your
own baseline or the device's own rating; tune to your fleet.
| Alert | Condition | Why it matters |
|---|---|---|
| Link down (unplanned) | network.interface.oper_status == 2 while network.interface.admin_status == 1 | The interface is administratively up but operationally down — an unplanned outage, not a config change. |
| Interface errors rising | rate(network.errors) > 0 / rising vs baseline | Errors climbing point to a link, duplex, or cabling fault. |
| Interface saturation | network.io rate approaching network.interface.speed | The link is near its nominal capacity; expect drops and queueing. |
| UPS on battery | ups.output.status == 3 (onBattery) | Utility power is lost; the load is now on battery runway. |
| UPS battery low / replace | ups.battery.status == 3 or ups.battery.replace_indicator == 2 | The battery is degraded or end-of-life; schedule a replacement. |
| UPS battery runway short | ups.battery.capacity dropping vs baseline / ups.battery.runtime_remaining falling | Remaining runtime is shrinking; plan a graceful shutdown before it hits zero. |
| UPS overload | ups.output.load high vs the unit's rated capacity | The UPS is carrying more than it should; shed load or upgrade. |
| Host memory pressure | system.memory.available dropping vs baseline | Free real memory is shrinking toward swap or OOM. |
| Host CPU saturation | system.cpu.load_average.1m rising above the core count | Sustained load above the core count means the run queue is backing up. |
| Device unreachable | sustained snmpreceiver scrape errors / no data for the device | The device stopped answering its OIDs — there is no up series, so the scrape error is the liveness signal. |
Access Setup
No code runs on the device. All you need is SNMP read access.
SNMPv2c — enable snmpd and set a read-only community:
rocommunity readonly_community_here default
sysLocation "rack-A1, row-3"
sysContact "ops@example.com"
Then restart snmpd and test from the host that will run the
Collector:
snmpwalk -v2c -c readonly_community_here -t 2 target-host:161 \
1.3.6.1.2.1.1.1
SNMPv3 — prefer authPriv with SHA/AES:
createUser monitor SHA "auth-key-here" AES "priv-key-here"
rouser monitor priv
For network gear, the vendor CLI equivalents apply (Cisco
snmp-server community, Junos snmp { community }, etc.). Always
scope communities and SNMPv3 users to the Collector host's source
address when the device supports it.
Configuration
The snmpreceiver takes one endpoint per receiver instance, so use
one instance per device (or per device class sharing credentials).
Metrics are defined by OID with an explicit unit and either
scalar_oids (values ending in .0) or column_oids (indexed
tables). Table rows fan out to separate resources via
resource_attributes.
receivers:
snmp/linux:
collection_interval: 30s
endpoint: udp://linux-host.internal:161
version: v2c
community: ${env:SNMP_COMMUNITY_LINUX}
resource_attributes:
device.id:
scalar_oid: "1.3.6.1.2.1.1.5.0" # sysName
cpu.index:
indexed_value_prefix: "cpu_"
metrics:
system.processes.count:
description: Number of processes running (hrSystemProcesses)
unit: "{processes}"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.2.1.25.1.6.0"
system.users.count:
description: Active user sessions (hrSystemNumUsers)
unit: "{users}"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.2.1.25.1.5.0"
system.memory.total:
description: Total real memory (UCD memTotalReal)
unit: KiBy
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.2021.4.5.0"
system.memory.available:
description: Available real memory (UCD memAvailReal)
unit: KiBy
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.2021.4.6.0"
system.memory.cached:
description: Cached memory (UCD memCached)
unit: KiBy
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.2021.4.11.0"
system.memory.buffered:
description: Buffered memory (UCD memBuffer)
unit: KiBy
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.2021.4.14.0"
system.cpu.load_average.1m:
description: "1-minute load average x 100 (UCD laLoadInt.1)"
unit: "1"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.2021.10.1.5.1"
system.cpu.load_average.5m:
description: "5-minute load average x 100 (UCD laLoadInt.2)"
unit: "1"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.2021.10.1.5.2"
system.cpu.load_average.15m:
description: "15-minute load average x 100 (UCD laLoadInt.3)"
unit: "1"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.2021.10.1.5.3"
system.cpu.utilization:
description: Per-CPU utilization (hrProcessorLoad)
unit: "%"
gauge: { value_type: int }
column_oids:
- oid: "1.3.6.1.2.1.25.3.3.1.2"
resource_attributes: [cpu.index]
snmp/router:
collection_interval: 30s
endpoint: udp://edge-router.internal:161
version: v2c
community: ${env:SNMP_COMMUNITY_ROUTER}
resource_attributes:
device.id:
scalar_oid: "1.3.6.1.2.1.1.5.0"
network.interface.name:
oid: "1.3.6.1.2.1.31.1.1.1.1" # ifName
attributes:
direction:
enum: [receive, transmit]
metrics:
system.network.interfaces.count:
description: Number of network interfaces (IF-MIB ifNumber)
unit: "{interfaces}"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.2.1.2.1.0"
network.io:
description: Interface I/O bytes (ifHCInOctets / ifHCOutOctets)
unit: By
sum:
value_type: int
monotonic: true
aggregation: cumulative
column_oids:
- oid: "1.3.6.1.2.1.31.1.1.1.6"
resource_attributes: [network.interface.name]
attributes:
- { name: direction, value: receive }
- oid: "1.3.6.1.2.1.31.1.1.1.10"
resource_attributes: [network.interface.name]
attributes:
- { name: direction, value: transmit }
network.errors:
description: Interface errors (ifInErrors / ifOutErrors)
unit: "{errors}"
sum:
value_type: int
monotonic: true
aggregation: cumulative
column_oids:
- oid: "1.3.6.1.2.1.2.2.1.14"
resource_attributes: [network.interface.name]
attributes:
- { name: direction, value: receive }
- oid: "1.3.6.1.2.1.2.2.1.20"
resource_attributes: [network.interface.name]
attributes:
- { name: direction, value: transmit }
network.interface.speed:
description: Interface nominal speed (ifHighSpeed)
unit: "Mbit/s"
gauge: { value_type: int }
column_oids:
- oid: "1.3.6.1.2.1.31.1.1.1.15"
resource_attributes: [network.interface.name]
network.interface.admin_status:
description: "ifAdminStatus - 1=up, 2=down, 3=testing"
unit: "1"
gauge: { value_type: int }
column_oids:
- oid: "1.3.6.1.2.1.2.2.1.7"
resource_attributes: [network.interface.name]
network.interface.oper_status:
description: "ifOperStatus - 1=up, 2=down, 3=testing, 4=unknown, 5=dormant"
unit: "1"
gauge: { value_type: int }
column_oids:
- oid: "1.3.6.1.2.1.2.2.1.8"
resource_attributes: [network.interface.name]
snmp/ups:
collection_interval: 30s
endpoint: udp://ups-dc01.internal:161
version: v2c
community: ${env:SNMP_COMMUNITY_UPS}
resource_attributes:
device.id:
scalar_oid: "1.3.6.1.2.1.1.5.0"
metrics:
ups.battery.status:
description: "Battery status - 1=unknown, 2=normal, 3=low"
unit: "1"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.2.1.1.0"
ups.battery.capacity:
description: Remaining battery capacity (upsAdvBatteryCapacity)
unit: "%"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.2.2.1.0"
ups.battery.temperature:
description: Battery temperature (upsAdvBatteryTemperature)
unit: "Cel"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.2.2.2.0"
ups.battery.runtime_remaining:
description: Estimated runtime remaining, centiseconds (TimeTicks)
unit: "cs"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.2.2.3.0"
ups.battery.replace_indicator:
description: "1=no replacement needed, 2=battery needs replacement"
unit: "1"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.2.2.4.0"
ups.input.voltage:
description: Input line voltage (upsAdvInputLineVoltage)
unit: "V"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.3.2.1.0"
ups.input.frequency:
description: Input line frequency (upsAdvInputFrequency)
unit: "Hz"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.3.2.4.0"
ups.output.status:
description: "Output status - 2=onLine, 3=onBattery, 4=onSmartBoost, ..."
unit: "1"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.4.1.1.0"
ups.output.voltage:
description: Output voltage (upsAdvOutputVoltage)
unit: "V"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.4.2.1.0"
ups.output.load:
description: Output load as percent of rated capacity
unit: "%"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.4.2.3.0"
ups.output.current:
description: Output current (upsAdvOutputCurrent)
unit: "A"
gauge: { value_type: int }
scalar_oids:
- oid: "1.3.6.1.4.1.318.1.1.1.4.2.4.0"
processors:
memory_limiter:
check_interval: 5s
limit_percentage: 80
spike_limit_percentage: 25
batch:
timeout: 10s
send_batch_size: 1024
# Static metadata per pipeline. snmpreceiver only attaches values
# it can retrieve via SNMP; constants like service.name and
# device.kind are added here. Each device gets a distinct
# service.name so it appears as its own service in Scout.
resource/linux:
attributes:
- { key: service.name, value: "linux-host-01", action: insert }
- { key: service.namespace, value: ${env:SERVICE_NAMESPACE}, action: insert }
- { key: deployment.environment.name, value: ${env:ENVIRONMENT}, action: insert }
- { key: environment, value: ${env:ENVIRONMENT}, action: insert }
- { key: device.kind, value: compute, action: insert }
- { key: device.manufacturer, value: "generic-linux", action: insert }
- { key: device.model.identifier, value: "net-snmp", action: insert }
- { key: site.id, value: ${env:SITE_ID}, action: insert }
resource/router:
attributes:
- { key: service.name, value: "cisco-router-01", action: insert }
- { key: service.namespace, value: ${env:SERVICE_NAMESPACE}, action: insert }
- { key: deployment.environment.name, value: ${env:ENVIRONMENT}, action: insert }
- { key: environment, value: ${env:ENVIRONMENT}, action: insert }
- { key: device.kind, value: network, action: insert }
- { key: device.manufacturer, value: "cisco", action: insert }
- { key: device.model.identifier, value: "ISR-C2900", action: insert }
- { key: site.id, value: ${env:SITE_ID}, action: insert }
resource/ups:
attributes:
- { key: service.name, value: "apc-ups-01", action: insert }
- { key: service.namespace, value: ${env:SERVICE_NAMESPACE}, action: insert }
- { key: deployment.environment.name, value: ${env:ENVIRONMENT}, action: insert }
- { key: environment, value: ${env:ENVIRONMENT}, action: insert }
- { key: device.kind, value: power, action: insert }
- { key: device.manufacturer, value: "apc", action: insert }
- { key: device.model.identifier, value: "Smart-UPS-SRT", action: insert }
- { key: site.id, value: ${env:SITE_ID}, action: insert }
exporters:
otlphttp/b14:
endpoint: ${env:OTEL_EXPORTER_OTLP_ENDPOINT}
tls:
insecure_skip_verify: true
service:
pipelines:
metrics/linux:
receivers: [snmp/linux]
processors: [memory_limiter, resource/linux, batch]
exporters: [otlphttp/b14]
metrics/router:
receivers: [snmp/router]
processors: [memory_limiter, resource/router, batch]
exporters: [otlphttp/b14]
metrics/ups:
receivers: [snmp/ups]
processors: [memory_limiter, resource/ups, batch]
exporters: [otlphttp/b14]
Environment Variables
SNMP_COMMUNITY_LINUX=readonly_community_here
SNMP_COMMUNITY_ROUTER=readonly_community_here
SNMP_COMMUNITY_UPS=readonly_community_here
# Service identity — each device pipeline stamps its own service.name
# (linux-host-01 / cisco-router-01 / apc-ups-01). These env vars
# apply to all three.
SERVICE_NAMESPACE=network-infra
ENVIRONMENT=demo
SITE_ID=your-site-id
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel.<your-tenant>.base14.io
Every metric carries a service.name/service.namespace/environment
set and the IoT device schema: device.id, device.kind
(compute / network / power), device.manufacturer,
device.model.identifier, site.id. Network gear is a compute
device with device.kind=network — there is no separate
network.device.* namespace. Each simulated (or real) device
appears as its own service in Scout because each receiver pipeline
stamps a distinct service.name.
Semconv version note:
deployment.environment.nameis the current OTel attribute (introduced in semantic conventions v1.27.0, stable as of v1.41.0). Scout's UI filters on the lowercaseenvironmentkey, so emit it alongside the OTel-nativedeployment.environment.name. The legacydeployment.environmentis still accepted for backward compatibility.
Shipping via a Local Scout Collector
The otlphttp/b14 exporter above ships directly to Scout. If you
already run a tenant-local Scout Collector — recommended when you
have multiple apps on the same host, need local buffering, or want
a single point of auth — forward metrics to it instead:
exporters:
otlphttp/upstream:
endpoint: http://otel-collector-base14:4318
Then join the Scout Collector's Docker network so DNS resolves the container name:
services:
otel-collector:
# ...
networks:
- default
- otel-collector-network
networks:
otel-collector-network:
external: true
For setting up that upstream Scout Collector, see Docker Compose Setup.
Verify the Setup
Start the Collector and check for successful scrapes within 60 seconds. From the host running the Collector:
# Probe each device reachability and credentials
snmpwalk -v2c -c "$SNMP_COMMUNITY_LINUX" -t 2 linux-host.internal:161 1.3.6.1.2.1.1.1
snmpwalk -v2c -c "$SNMP_COMMUNITY_ROUTER" -t 2 edge-router.internal:161 1.3.6.1.2.1.31.1.1.1.6
snmpwalk -v2c -c "$SNMP_COMMUNITY_UPS" -t 2 ups-dc01.internal:161 1.3.6.1.4.1.318.1.1.1.2.2
# Confirm the Collector emitted metrics
docker logs otel-collector 2>&1 | grep -E 'Name: (network\.io|ups\.|system\.)'
Troubleshooting
no such host or DNS lookup failures
Cause: The Collector cannot resolve the device hostname, or a Docker network isolates it from the device.
Fix:
- Use an IP literal in
endpointif DNS is not available inside the Collector container - On Docker Desktop, use
host.docker.internalto reach services on the host - Run the Collector with
network_mode: hostif the target is on the host's network segment
Request timeout on scrape
Cause: Device is slow to respond, or UDP packets are being dropped.
Fix:
- Raise
timeout: 10son the receiver block - Lower
collection_intervalpressure if the device is resource- constrained (default 30s is usually fine) - Verify UDP/161 is not blocked by an ACL or firewall between Collector and device
No Such Instance for Counter64 OIDs
Cause: The device does not support the ifXTable (IF-MIB
high-capacity counters), common on older hardware.
Fix:
Fall back to 32-bit counters:
column_oids:
- oid: "1.3.6.1.2.1.2.2.1.10" # ifInOctets (Counter32)
resource_attributes: [network.interface.name]
attributes:
- { name: direction, value: receive }
Expect wraparound on high-throughput interfaces; as a Counter32 in octets, ifInOctets wraps at 2^32 bytes (~4 GiB), so a busy link can roll over in seconds.
Metric values look wrong by a factor of 10 or 100
Cause: Several MIBs store fixed-point values as scaled integers
(for example, UCD laLoadInt uses ×100; PowerNet upsAdvOutputFrequency
uses units of 0.1 Hz). upsAdvBatteryRunTimeRemaining is a
TimeTicks value — centiseconds, not seconds — so a 30-minute
runtime reads as 180000. The Operational
ups.battery.runtime_remaining and the Diagnostic
ups.input.frequency are the series this most often bites.
Look at: the raw system.cpu.load_average.* and
ups.battery.runtime_remaining values against what the device's own
CLI reports — a clean ×100 or centisecond offset confirms it is a
scaling issue, not a bad OID.
Fix:
- Check the MIB definition for the OID's
UNITSclause - Document the scale in the metric
description, or - Apply a
transformprocessor to rescale before export
Scrape errors flood the log during a device outage
Cause: The receiver logs each failed scrape at error level. With
no up series, a sustained run of these entries is the
device-unreachable signal.
Look at: whether the affected pipeline's metrics go stale in
Scout (network.io, ups.output.status, or system.memory.available
stop updating) — that, alongside the scrape errors, confirms the
device is down rather than a transient packet drop.
Fix:
The Collector pipeline keeps running — the error entries are
expected and recoverable. To quiet them, increase
collection_interval for the flaky receiver, or route the
Collector's own logs through a filter/logs processor that drops
snmp scrape errors.
FAQ
Does this work with SNMPv3?
Yes. Set version: v3 and provide user, security_level,
auth_type, auth_password, privacy_type, and privacy_password
as appropriate. Prefer authPriv with SHA-256 and AES-256 on
production gear.
How do I monitor many similar devices without repeating YAML?
snmpreceiver does not have a native templating feature, but the
Collector config can be rendered from a template (Jinja, envsubst,
Helm). Generate one snmp/<device> block per target and share the
metric definitions via YAML anchors, or manage the config as code
and let the generator emit identical metric blocks.
Can I receive SNMP traps with this receiver?
No. snmpreceiver is poll-only, and the Collector has no SNMP-trap
receiver (the request to add one was declined). To capture traps,
forward them into the Collector as logs via snmptrapd + a file or
syslog receiver.
What's the difference between using scalar_oid vs oid under
resource_attributes?
scalar_oid fetches a single value (must end in .0) and stamps
every metric from this receiver with it — use it for device-wide
attributes like device.id from sysName. oid points to a
column and produces per-index resources — use it for things that
vary per row like interface names.
Why is device.kind set by a processor and not the receiver?
snmpreceiver can only attach values it fetches over SNMP. Static
labels like device.kind=network don't exist on the device, so we
add them with a resource processor scoped to each device
pipeline. This keeps each pipeline self-contained.
Can I map string values (DisplayString) to numeric metrics?
No. A numeric metric needs a numeric SNMP type (Integer, Counter*,
Gauge32, TimeTicks); snmpreceiver can read a DisplayString only as a
resource-attribute value, not as a metric. If a MIB returns a
DisplayString like UCD laLoad ("0.21"), switch to the
integer-scaled sibling OID (laLoadInt, ×100) and document the scaling
in the metric description.
Does polling add significant load on devices?
A 30-second collection_interval against a dozen scalar OIDs and
a single interface table is negligible for modern network gear.
For very large ifTable walks on older devices, raise the
interval or reduce the column list.
Related Guides
- OTel Collector Configuration — Advanced Collector configuration
- Docker Compose Setup — Run the Collector locally
- Docker Monitoring — Host-level container metrics alongside network gear
- HAProxy Monitoring — Monitor the load balancer in front of those network devices
- IoT & Edge Instrumentation — MQTT, Sparkplug B, OPC-UA, and edge Collector patterns beyond SNMP
What's Next?
- Create Dashboards: Start with interface throughput, CPU load, and UPS battery capacity panels. See Create Your First Dashboard
- Add more devices: Drop in a switch MIB, a PDU MIB, or a
printer MIB — the
snmpreceiverpattern is the same. The runnable example at components/snmp-telemetry shows how to add a fourth device. - Broaden IoT coverage: SNMP is the first hop into IoT monitoring. See the IoT & Edge instrumentation guides for MQTT, Sparkplug B, and OPC-UA paths when you need protocols SNMP cannot reach.