What did you do?
Ran ipmi_exporter v1.10.1 in Kubernetes with 500 targets, 4 collectors
(ipmi, dcmi, chassis, bmc), scrape_interval=15s, container memory limit 500Mi.
What did you expect to see?
Stable memory usage, or at least a plateau well below the limit.
What did you see instead?
Linear memory growth over ~15 hours until OOMKill. Reproducible — both
replicas started simultaneously followed identical trajectories and
OOMKilled within the same second.
- Pod A: 37 MiB → 461 MiB → OOMKill → restart
- Pod B: 22 MiB → 452 MiB → OOMKill → restart
- Growth rate: ~30 MiB/hour
Diagnosis
The growth is not in Go memory. Go metrics are flat:
go_memstats_heap_inuse_bytes: 12 MB
go_memstats_sys_bytes: 49 MB
The growth is in kernel slab (dentry/inode cache). From
/sys/fs/cgroup/memory.stat at ~223 MiB working set:
anon: 19 MB (process memory, flat)
file: 0.9 MB
kernel_stack: 2 MB
slab_reclaimable: 192 MB ← the growth
slab_unreclaimable: 1.5 MB
Other observations:
- Only 5–6 live freeipmi subprocesses at any moment (no subprocess leak)
/tmp contains 1 file (no temp-file leak)
GOMEMLIMIT=156MiB and GOGC=80 had no effect (confirms it isn't Go heap)
Hypothesized cause
Each scrape creates a unique /tmp/ipmi_exporter-<hash> config file and
forks freeipmi binaries (ipmi-sensors, ipmi-dcmi, ipmi-chassis,
bmc-info), each loading ~15 shared libraries. At 500 targets × 4
collectors × 15s interval, that's ~130 fork+exec per second, generating
thousands of dentry/inode allocations per second. Kernel caches them
as slab_reclaimable. Under sustained churn near the memory limit,
reclaim can't keep pace → OOMKill.
Suggested fix (AI)
Avoid per-scrape temp file churn in
freeipmi.go:
- Use
memfd_create(2) for the config — no dentry/inode, lives in memory.
- Or generate the config file once per target at startup and reuse it.
Environment
- ipmi_exporter: v1.10.1
- freeipmi:
- Kubernetes:
- Container runtime: containerd
- cgroup: v2
- Kernel: 6.8.0-100-generic (Alpine Linux v3.22)
- Target count: 500
- Scrape interval: 15s
- Collectors: ipmi, dcmi, chassis, bmc
Note: we also see this reported which possibly related
What did you do?
Ran ipmi_exporter v1.10.1 in Kubernetes with 500 targets, 4 collectors
(ipmi, dcmi, chassis, bmc), scrape_interval=15s, container memory limit 500Mi.
What did you expect to see?
Stable memory usage, or at least a plateau well below the limit.
What did you see instead?
Linear memory growth over ~15 hours until OOMKill. Reproducible — both
replicas started simultaneously followed identical trajectories and
OOMKilled within the same second.
Diagnosis
The growth is not in Go memory. Go metrics are flat:
go_memstats_heap_inuse_bytes: 12 MBgo_memstats_sys_bytes: 49 MBThe growth is in kernel slab (dentry/inode cache). From
/sys/fs/cgroup/memory.statat ~223 MiB working set:Other observations:
/tmpcontains 1 file (no temp-file leak)GOMEMLIMIT=156MiBandGOGC=80had no effect (confirms it isn't Go heap)Hypothesized cause
Each scrape creates a unique
/tmp/ipmi_exporter-<hash>config file andforks freeipmi binaries (
ipmi-sensors,ipmi-dcmi,ipmi-chassis,bmc-info), each loading ~15 shared libraries. At 500 targets × 4collectors × 15s interval, that's ~130 fork+exec per second, generating
thousands of dentry/inode allocations per second. Kernel caches them
as
slab_reclaimable. Under sustained churn near the memory limit,reclaim can't keep pace → OOMKill.
Suggested fix (AI)
Avoid per-scrape temp file churn in
freeipmi.go:
memfd_create(2)for the config — no dentry/inode, lives in memory.Environment
Note: we also see this reported which possibly related