By the SNMP Monitoring team · Reviewed July 2026
CPU over SNMP is easy to wire and easy to get wrong. One sample at 97%, page goes off, box was just compiling something for forty seconds. Or you graph "CPU" off a single index on a 32-core host and wonder why the app is on fire while the dashboard looks bored.
This write-up is the ops path: which object, how to walk it, how to average cores without lying to yourself, when load average saves you, and how to set thresholds that don't train people to ignore alerts. Full leaf dump lives on the twin page SNMP CPU OIDs. Parent: server monitoring.
Which OID gives CPU usage
On boring servers with HOST-RESOURCES-MIB, the object you actually want is hrProcessorLoad:
- OID:
1.3.6.1.2.1.25.3.3.1.2 - Rough meaning: how busy that processor looked over a short recent window (agent-defined—often closer to a minute than a second)
- Shape: table column, one row per logical CPU, integers 0–100
There is no blessed "total CPU %" scalar in HOST-RESOURCES. You invent total. Usual recipe: average every row you got back. On multi-core iron, watching only .1 (or the first index your NMS cached) is how you miss a single pegged vCPU while the rest nap.
Linux Net-SNMP boxes often also speak UCD load average under 1.3.6.1.4.1.2021.10.1.3 (1 / 5 / 15 min instances). That's not percent. It's run-queue style load. Different question: "are we queued up?" vs "how hot are the cores?"
I use both when the agent has them:
- hrProcessorLoad — capacity / busy %
- laLoad — saturation / "work is stacking up"
- Together — fewer stupid pages
Names and raw counters: CPU OIDs. Load-focused how-to: system load.
Reading it with snmpwalk
Don't start in the GUI. Start in a shell where you can see errors.
snmpwalk -v2c -c <RO_string> <host> 1.3.6.1.2.1.25.3.3.1.2
Four-core example that actually looks like production output:
HOST-RESOURCES-MIB::hrProcessorLoad.196608 = INTEGER: 12
HOST-RESOURCES-MIB::hrProcessorLoad.196609 = INTEGER: 47
HOST-RESOURCES-MIB::hrProcessorLoad.196610 = INTEGER: 9
HOST-RESOURCES-MIB::hrProcessorLoad.196611 = INTEGER: 33
Those big indexes? Opaque row IDs. Don't map them to "CPU0" in your head without checking. Values are whole percents. Empty walk means wrong string, ACL, MIB not built in, or your view/allowlist chopped the branch—not "CPU is zero."
<RO_string> stays a placeholder on purpose. Flags and bulk walks: snmpwalk.

Averaging across cores
Managers love one line on a graph. Fine. Build it like an adult:
- Pull every
hrProcessorLoadinstance (walk or GetBulk on the column). - Add them up.
- Divide by how many rows came back. That count is N for this sample.
- Store the average as host CPU for that interval.
From the dump above: (12+47+9+33)/4 = 25.25. Round however your TSDB likes.
Corners that bite:
- N changes (hotplug, weird container views). If N halves overnight, ask why before you trust the average.
- One core at 100 with the rest idle still averages "meh." For sticky single-thread apps, graph max core too.
- Never average load-average numbers with percent numbers. Units don't mix.
Quick sanity before paging humans: does N match the vCPU count you think that VM has? If the agent says 2 and the hypervisor says 16, fix that first.
Load average as an alternative signal
Sometimes percent looks fine and the box still feels dead. Load average is the other knob.
snmpwalk -v2c -c <RO_string> <host> 1.3.6.1.4.1.2021.10.1.3
Three instances if UCD is happy—1, 5, 15 minute. On a 4-vCPU host, 5-minute load hanging around 4 means the run queue is roughly keeping the CPUs fed; north of that, stuff waits. That's a different story than "average hrProcessorLoad is 68%."
| What you're asking | What I'd poll |
|---|---|
| How busy are the cores? | Average of hrProcessorLoad |
| Is work backing up? | 5- or 15-minute load average |
| Can I sleep through the night? | Both, with multi-sample rules |
More on load: monitor system load, load OIDs.
Setting CPU alert thresholds
Single-sample CPU alerts are how you train the team to mute the channel.
What tends to work:
- Warn when average CPU stays ≥ ~80% across several polls (think 5–10 minutes wall clock, not one scrape)
- Crit when average stays ≥ ~95% for a shorter multi-sample window, or max core glued at 100% on hosts where one thread kills the app
- Optional buddy signal: 5-minute load above vCPU count for more than one or two intervals
What doesn't:
- Page on a lone spike during backup windows
- Copy "CPU > 90%" from a vendor PDF with a 30-second poll and no duration
- Ignore steal time / noisy neighbors on shared hypervisors (SNMP won't always show steal—know your stack)
Routing, hysteresis, quiet hours: alerts. Stick a runbook link next to the alert or you'll get "reboot it?" forever.
Full OID reference
I'm deliberately not pasting the whole leaf list here. How-to stays how-to.
Bookmark that from the dashboard so 2 a.m. you isn't reverse-engineering MIB names half-asleep.
Monitoring CPU externally
Your on-prem poller only sees the host if the path to UDP 161 is up and the agent is alive and the poller itself isn't sharing fate with the site. That's necessary. It isn't the whole reliability story.
An external SNMPv2c check—ostr.io is the one we document on this site—hits the host from outside and can read CPU via 1.3.6.1.2.1.25.3.3.1.2 along with the other metrics that product actually supports (uptime, sessions, processes, load, memory, disks, interfaces). Random community, OID allowlist, dual cadence if you're doing their double-durability pattern. Bigger picture: external monitoring, setup notes: add SNMP endpoint.
One external path in the runbook. Not five "maybe" SaaS trials with public still hanging around.
Frequently asked questions
What OID is CPU usage in SNMP?
Servers with HOST-RESOURCES: 1.3.6.1.2.1.25.3.3.1.2 (hrProcessorLoad), per core, 0–100. No universal total leaf—average the rows. Routers and weird appliances may only speak vendor enterprise OIDs; walk first, template second.
How do I check CPU load via SNMP?
snmpwalk -v2c -c <RO_string> <host> 1.3.6.1.2.1.25.3.3.1.2
For load average instead of percent (Net-SNMP/UCD):
snmpwalk -v2c -c <RO_string> <host> 1.3.6.1.4.1.2021.10.1.3
When the CLI is clean, point the NMS at the same OIDs.
How do I average CPU across cores?
Sum every hrProcessorLoad instance, divide by how many you got. That's host-level utilization for that sample. Track max core on the side if single-thread stalls matter.
CPU load vs utilization—what's the difference?
Utilization here ≈ percent busy (hrProcessorLoad). Load average ≈ how much runnable work is stacked (not capped at 100). You can have "okay" percent with nasty load if things wait, or high percent with load that still matches CPU count on a well-sized box. If the agent exposes both, graph both.
Key takeaways
- Poll
hrProcessorLoad(1.3.6.1.2.1.25.3.3.1.2); average the rows for host CPU. - Prove it with
snmpwalkbefore the pretty dashboard. - Add UCD load (
1.3.6.1.4.1.2021.10.1.3) when queueing matters. - Alert on sustained multi-sample pain, not one blip—see alerts.
- Leaves and types: sensors/cpu. Procedure stays here.
- Off-box check (e.g. ostr.io) is a complement to the primary NMS, not a religion.