Introduction
Cable internet subscribers often have no visibility into their modem’s health beyond “the internet works” or “the internet doesn’t work.” But DOCSIS modems expose rich diagnostic data via SNMP — signal-to-noise ratios, power levels, uncorrectable codewords, and channel bonding status. With self-hosted monitoring tools, you can track this data over time, correlate modem health with internet performance, and even generate evidence for ISP support tickets. This guide compares three approaches to self-hosted DOCSIS modem monitoring: Prometheus SNMP Exporter, Telegraf with SNMP input, and custom Python SNMP collectors.
| Feature | Prometheus SNMP Exporter | Telegraf SNMP | Custom Python (pysnmp) |
|---|
| Setup Complexity | Medium (generator required) | Low (config file) | High (custom code) |
| Visualization | Grafana dashboards | InfluxDB + Grafana | Flexible (any backend) |
| Alerting | Prometheus AlertManager | Kapacitor / Grafana | Custom |
| SNMP v3 Support | Yes | Yes | Yes (via pysnmp) |
| Scalability | High (pull model) | Medium (agent-based) | Low (single instance) |
| DOCSIS MIBs | Manual MIB loading | Auto-discovery | Manual parsing |
| Learning Curve | Moderate | Low | High |
Understanding DOCSIS Modem Metrics
DOCSIS (Data Over Cable Service Interface Specification) modems expose key metrics via SNMP that indicate line quality:
Critical Metrics to Monitor
| Metric | OID | Healthy Range | What It Means |
|---|
| Downstream Power | .1.3.6.1.2.1.10.127.1.1.1.1.6 | -7 to +7 dBmV | Signal strength from ISP |
| Downstream SNR | .1.3.6.1.2.1.10.127.1.1.4.1.5 | >33 dB | Signal quality (higher is better) |
| Upstream Power | .1.3.6.1.2.1.10.127.1.2.2.1.3 | 35-50 dBmV | Signal strength to ISP |
| Uncorrectable Errors | .1.3.6.1.2.1.10.127.1.1.4.1.4 | 0 (or near zero) | Data corruption events |
| Corrected Errors | .1.3.6.1.2.1.10.127.1.1.4.1.3 | Low and stable | Layer 1 recovery events |
| Channel Count | .1.3.6.1.2.1.10.127.1.1.2.1.1 | 16-32+ downstream | Bonded channels for speed |
Why Monitor Your Modem?
- Diagnose intermittent issues: Power fluctuations or SNR drops often precede outages
- Evidence for ISP tickets: Historical graphs of signal degradation are hard for ISPs to dismiss
- Track upgrades: Confirm when your ISP adds channels or upgrades DOCSIS versions
- Correlate with speed tests: Link performance drops to specific modem metrics
Approach 1: Prometheus SNMP Exporter
The Prometheus SNMP Exporter is the most scalable option. It uses a generator to create SNMP configurations from MIB files, then exposes metrics in Prometheus format.
Docker Compose Setup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| version: "3.8"
services:
snmp-exporter:
image: prom/snmp-exporter:v0.24.1
container_name: snmp-exporter
volumes:
- ./snmp.yml:/etc/snmp_exporter/snmp.yml:ro
ports:
- "9116:9116"
restart: unless-stopped
prometheus:
image: prom/prometheus:v2.52.0
container_name: prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- prometheus_data:/prometheus
ports:
- "9090:9090"
restart: unless-stopped
grafana:
image: grafana/grafana:11.0.0
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards:ro
ports:
- "3000:3000"
restart: unless-stopped
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
prometheus_data:
grafana_data:
|
Generating the SNMP Configuration
1
2
3
4
5
| # Download DOCSIS MIBs
wget https://raw.githubusercontent.com/prometheus/snmp_exporter/main/generator/Makefile
# Generate SNMP config for DOCSIS modems
docker run -v $(pwd):/opt prom/snmp-generator generate --snmp.module=docsis_modem --snmp.walk=1.3.6.1.2.1.10.127
|
Prometheus Scrape Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # prometheus.yml
scrape_configs:
- job_name: "docsis_modem"
scrape_interval: 60s
static_configs:
- targets:
- "192.168.100.1" # Cable modem IP
metrics_path: /snmp
params:
module: [docsis_modem]
relabel_configs:
- source_labels: [__address__]
target_label: __param_target
- source_labels: [__param_target]
target_label: instance
- target_label: __address__
replacement: snmp-exporter:9116
|
Sample Grafana Dashboard Query
1
2
3
4
5
6
7
8
| # Downstream power levels (should be -7 to +7 dBmV)
docsIfDownChannelPower{instance="192.168.100.1"}
# Uncorrectable error rate
rate(docsIfDownChannelCorrectedErrors{instance="192.168.100.1"}[5m])
# Signal-to-noise ratio health check
(docsIfSigQSignalNoise{instance="192.168.100.1"} < 33)
|
Telegraf offers the simplest setup for beginners. Its SNMP input plugin supports DOCSIS MIBs out of the box with auto-table discovery.
Docker Compose Setup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| version: "3.8"
services:
telegraf:
image: telegraf:1.31
container_name: telegraf
volumes:
- ./telegraf.conf:/etc/telegraf/telegraf.conf:ro
restart: unless-stopped
influxdb:
image: influxdb:2.7
container_name: influxdb
volumes:
- influxdb_data:/var/lib/influxdb2
ports:
- "8086:8086"
restart: unless-stopped
environment:
- DOCKER_INFLUXDB_INIT_MODE=setup
- DOCKER_INFLUXDB_INIT_USERNAME=admin
- DOCKER_INFLUXDB_INIT_PASSWORD=admin123
- DOCKER_INFLUXDB_INIT_ORG=homelab
- DOCKER_INFLUXDB_INIT_BUCKET=modem_metrics
- DOCKER_INFLUXDB_INIT_ADMIN_TOKEN=my-super-secret-token
grafana:
image: grafana/grafana:11.0.0
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
ports:
- "3000:3000"
restart: unless-stopped
volumes:
influxdb_data:
grafana_data:
|
Telegraf SNMP Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
| # telegraf.conf
[agent]
interval = "60s"
flush_interval = "30s"
# DOCSIS modem SNMP polling
[[inputs.snmp]]
agents = ["192.168.100.1"]
version = 2
community = "public"
interval = "60s"
timeout = "10s"
retries = 3
# Downstream channels
[[inputs.snmp.table]]
oid = "1.3.6.1.2.1.10.127.1.1.1.1"
name = "docsIfDownChannel"
[[inputs.snmp.table]]
oid = "1.3.6.1.2.1.10.127.1.1.4.1"
name = "docsisDownstreamMetrics"
# Upstream channels
[[inputs.snmp.table]]
oid = "1.3.6.1.2.1.10.127.1.2.2.1"
name = "docsisUpstreamMetrics"
# Get interface statistics
[[inputs.snmp.field]]
oid = "1.3.6.1.2.1.2.2.1.10"
name = "ifInOctets"
[[inputs.snmp.field]]
oid = "1.3.6.1.2.1.2.2.1.16"
name = "ifOutOctets"
# Output to InfluxDB
[[outputs.influxdb_v2]]
urls = ["http://influxdb:8086"]
token = "my-super-secret-token"
organization = "homelab"
bucket = "modem_metrics"
|
Approach 3: Custom Python SNMP Collector
For maximum flexibility, a custom Python collector using pysnmp gives you complete control over metric collection and storage.
Python Collector Script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
| #!/usr/bin/env python3
"""
DOCSIS modem SNMP collector — polls modem metrics and pushes to
Prometheus Pushgateway or writes to InfluxDB directly.
"""
import time
import json
from pysnmp.hlapi import (
SnmpEngine, CommunityData, UdpTransportTarget,
ContextData, ObjectType, ObjectIdentity, nextCmd, getCmd
)
# DOCSIS OIDs to monitor
METRICS = {
# Downstream: (OID, name, unit, warning_threshold)
"down_power": ("1.3.6.1.2.1.10.127.1.1.1.1.6", "dBmV", (-7, 7)),
"down_snr": ("1.3.6.1.2.1.10.127.1.1.4.1.5", "dB", (33, None)),
"up_power": ("1.3.6.1.2.1.10.127.1.2.2.1.3", "dBmV", (35, 50)),
"uncorrectable": ("1.3.6.1.2.1.10.127.1.1.4.1.4", "errors", (0, 0)),
"corrected": ("1.3.6.1.2.1.10.127.1.1.4.1.3", "errors", (None, 1000)),
}
def poll_modem(host="192.168.100.1", community="public"):
"""Walk DOCSIS OIDs and return structured data."""
results = {}
for metric_name, (oid, unit, thresholds) in METRICS.items():
values = []
base_oid = ObjectIdentity(oid)
for (error_indication, error_status, error_index, var_binds) in nextCmd(
SnmpEngine(),
CommunityData(community, mpModel=1),
UdpTransportTarget((host, 161), timeout=2, retries=2),
ContextData(),
ObjectType(base_oid),
lexicographicMode=False,
):
if error_indication:
print(f"SNMP error: {error_indication}")
break
for var_bind in var_binds:
oid_str, val = str(var_bind[0]), var_bind[1]
values.append({
"oid": oid_str,
"value": float(val.prettyPrint()),
"unit": unit,
})
results[metric_name] = values
return results
def check_thresholds(results):
"""Check if any metrics are outside healthy ranges."""
alerts = []
for metric_name, values in results.items():
_, _, (low, high) = METRICS.get(metric_name, (None, None, (None, None)))
for entry in values:
val = entry["value"]
if low is not None and val < low:
alerts.append(f"LOW {metric_name}: {val} (min: {low})")
if high is not None and val > high:
alerts.append(f"HIGH {metric_name}: {val} (max: {high})")
return alerts
if __name__ == "__main__":
data = poll_modem()
alerts = check_thresholds(data)
print(json.dumps(data, indent=2))
if alerts:
print("
⚠️ Alerts:")
for alert in alerts:
print(f" - {alert}")
|
Docker Compose for Custom Collector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| version: "3.8"
services:
modem-collector:
build: .
container_name: modem-collector
volumes:
- ./metrics:/metrics
environment:
- MODEM_IP=192.168.100.1
- MODEM_COMMUNITY=public
- POLL_INTERVAL=60
restart: unless-stopped
pushgateway:
image: prom/pushgateway:v1.8.0
container_name: pushgateway
ports:
- "9091:9091"
restart: unless-stopped
|
Setting Up Alerting
Regardless of which approach you choose, here are the key alerts to configure:
Critical Alerts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| # Sample Prometheus alert rules for DOCSIS modem
groups:
- name: docsis_modem
rules:
- alert: DownstreamPowerOutOfRange
expr: docsIfDownChannelPower < -7 or docsIfDownChannelPower > 7
for: 10m
annotations:
summary: "Downstream power {{ $value }} dBmV is out of range"
- alert: LowSNR
expr: docsIfSigQSignalNoise < 33
for: 5m
annotations:
summary: "Downstream SNR {{ $value }} is below healthy threshold"
- alert: UncorrectableErrorsIncreasing
expr: rate(docsIfDownChannelUncorrectedErrors[5m]) > 10
for: 5m
annotations:
summary: "Uncorrectable error rate {{ $value }}/s indicates line issues"
- alert: ChannelLoss
expr: count(docsIfDownChannelId) < 8
for: 15m
annotations:
summary: "Less than 8 downstream channels bonded — possible modem reset or line issue"
|
For related reading, see our guides on SNMP collectors with snmp_exporter, snmpcollector, and Telegraf, Prometheus multi-cluster federation monitoring, and self-hosted bandwidth monitoring tools.
FAQ
Q: My modem doesn’t respond to SNMP on 192.168.100.1. What’s wrong?
Many consumer routers block SNMP traffic between LAN and the modem’s management IP. Try connecting a computer directly to the modem (bypassing the router) to confirm SNMP works. Alternatively, some routers allow you to configure a static route to the modem’s IP. Use snmpwalk -v2c -c public 192.168.100.1 to test connectivity.
Q: Do I need DOCSIS MIBs to monitor my modem?
Not strictly. You can poll OIDs directly without MIBs — the numeric OIDs work fine. MIBs only provide human-readable names and type information. For Grafana dashboards, numeric OIDs are sufficient as long as you document what each metric represents.
Q: Can this monitoring detect when my ISP is throttling me?
Indirectly. If you see sudden drops in bonded channels or SNR degradation that correlate with speed test results, you have evidence. However, DOCSIS monitoring shows physical layer metrics, not traffic shaping. Combine modem monitoring with speed test tools like iperf3 or Speedtest CLI for a complete picture.
Yes. Prometheus SNMP Exporter and Telegraf both support SNMP v3 with authentication and encryption. For Prometheus, specify version: 3 in your module config with auth_protocol, priv_protocol, and credentials. For Telegraf, set version = 3 in the [[inputs.snmp]] block with appropriate auth parameters.
Q: How much storage do I need for modem metrics?
A single DOCSIS modem generates about 50-100 metrics per poll at 60-second intervals. This translates to roughly 5-10 MB per month in Prometheus or 15-30 MB in InfluxDB. With a 30-day retention policy, budget about 50-100 MB for a comfortable margin.
Q: Can I monitor multiple modems (e.g., for a small ISP or MDU)?
Absolutely. Each approach scales: Prometheus SNMP Exporter handles thousands of targets natively. Telegraf can run multiple [[inputs.snmp]] blocks for different modems. The Python collector can iterate over a list of IPs. For multi-modem deployments, tag each modem with a location or customer identifier in Grafana.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com