Summary
The admin-panel container's main process (bun server.ts) holds a steady ~8% of one CPU core even with no traffic. Diagnosis points to an internal 1-second telemetry/self-monitoring timer, not to deployment, networking, or the healthcheck.
Environment
- Runtime: Bun (
bun run start → bun server.ts)
- Container:
admin-panel (LibreChat), rootless Podman
- Host: Fedora / RHEL-based, cgroups v2
- Observed: idle (no user traffic)
Symptom
podman top shows the server process holding a constant CPU average with no incoming requests:
USER PID PPID %CPU ELAPSED ARGS
bun 1 0 0.000 1h11m21s bun run start
bun 3 1 7.848 1h11m21s (5m36s CPU) bun server.ts
CPU time accounting confirms it is continuous, not a stale average:
5m36s CPU / 1h11m21s elapsed ≈ 7.8% — the process has been burning ~8% of a core since startup.
Investigation
All diagnostics were done by reading /proc/<pid>/ — no strace/perf needed.
1. Current syscall — process sits in the event loop:
Sampling /proc/<pid>/syscall returns syscall 441 (epoll_pwait2) almost every time, occasionally caught in running. This is the classic signature of an event loop being woken periodically by a timer — not file-watcher polling (which would show statx/read floods) and not CPU-bound work.
2. File descriptors — three timers, one relevant, plus a self-memory read:
4 -> anon_inode:[eventpoll] # epoll instance
5 -> anon_inode:[timerfd] # disarmed
6 -> anon_inode:[eventfd] # libuv loop wakeup (normal)
7 -> anon_inode:[timerfd] # disarmed
8 -> anon_inode:[timerfd] # ACTIVE
10 -> /proc/3/statm # persistent fd to the process's OWN memory stats
11 -> socket:[...] # single socket
The persistent open fd to /proc/3/statm (PID 3 = bun server.ts itself, inside the container namespace) indicates the process periodically reads its own memory usage → self-monitoring / telemetry pattern.
3. Timer period — exactly 1 second:
fdinfo for the active timer (fd 8):
it_value: (0, 726404510) # next fire in ~726 ms
it_interval: (1, 0) # re-arms every 1.000 s, indefinitely
The other two timerfds (fd 5, fd 7) are disarmed (it_interval: (0, 0)).
Root cause
A setInterval(..., 1000)-style loop in the application wakes the event loop once per second (~86,400 times/day) and reads process memory via /proc/self/statm. The statm read itself is cheap; the ~8% cost comes from the work hanging off each tick — likely metric serialization, object allocation (GC pressure), and/or pushing stats to a live dashboard. This matches an admin panel that surfaces real-time metrics.
Ruled out:
- Healthcheck — that's a separate transient
bun -e "fetch(...)" process, not PID 3.
- File watcher (
--watch/--hot) — no disk-polling syscalls; only one active timer.
- Reconnection loop — a single, stable socket; no cycling connection attempts.
Impact / workaround
Purely application-level; deployment is unaffected. As an interim mitigation the container can be capped (e.g. --cpus 0.5) to bound the impact without code changes, but the real fix is the interval.
How to reproduce the diagnosis
# Find the host PID of the server process
podman top admin-panel pid hpid args # take hpid of `bun server.ts`
# Confirm event loop + timer signature
ls -l /proc/<hpid>/fd/ # look for active timerfd
grep -H '' /proc/<hpid>/fdinfo/<timerfd> # read it_interval
Summary
The
admin-panelcontainer's main process (bun server.ts) holds a steady ~8% of one CPU core even with no traffic. Diagnosis points to an internal 1-second telemetry/self-monitoring timer, not to deployment, networking, or the healthcheck.Environment
bun run start→bun server.ts)admin-panel(LibreChat), rootless PodmanSymptom
podman topshows the server process holding a constant CPU average with no incoming requests:CPU time accounting confirms it is continuous, not a stale average:
5m36s CPU / 1h11m21s elapsed ≈ 7.8%— the process has been burning ~8% of a core since startup.Investigation
All diagnostics were done by reading
/proc/<pid>/— nostrace/perfneeded.1. Current syscall — process sits in the event loop:
Sampling
/proc/<pid>/syscallreturns syscall 441 (epoll_pwait2) almost every time, occasionally caught inrunning. This is the classic signature of an event loop being woken periodically by a timer — not file-watcher polling (which would showstatx/readfloods) and not CPU-bound work.2. File descriptors — three timers, one relevant, plus a self-memory read:
The persistent open fd to
/proc/3/statm(PID 3 =bun server.tsitself, inside the container namespace) indicates the process periodically reads its own memory usage → self-monitoring / telemetry pattern.3. Timer period — exactly 1 second:
fdinfofor the active timer (fd 8):The other two timerfds (fd 5, fd 7) are disarmed (
it_interval: (0, 0)).Root cause
A
setInterval(..., 1000)-style loop in the application wakes the event loop once per second (~86,400 times/day) and reads process memory via/proc/self/statm. Thestatmread itself is cheap; the ~8% cost comes from the work hanging off each tick — likely metric serialization, object allocation (GC pressure), and/or pushing stats to a live dashboard. This matches an admin panel that surfaces real-time metrics.Ruled out:
bun -e "fetch(...)"process, not PID 3.--watch/--hot) — no disk-polling syscalls; only one active timer.Impact / workaround
Purely application-level; deployment is unaffected. As an interim mitigation the container can be capped (e.g.
--cpus 0.5) to bound the impact without code changes, but the real fix is the interval.How to reproduce the diagnosis