You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On 2026-07-10 07:06 KST the Proxmox host server-1 ran out of physical RAM and the Linux kernel OOM-killer terminated the KVM guest process for the baobab k8s-node worker VM (the largest-RSS process on the box). The single k8s worker node backing our baobab cluster disappeared, and every orakl pod scheduled there went Pending/Terminating for ~2h55m. This is an infrastructure-layer (Proxmox host memory overcommit) problem, not an orakl application bug: the sum of VM memory reservations on server-1 (~72 GiB) exceeds the host's physical RAM (67.4 GiB), so the host had no headroom and killed a whole guest. It is distinct from the long-standing pod-level orakl-node memory leak, which self-heals via container restart and did not fire here.
Impact
Outage window: ~2h55m. k8s-node went NotReady ("Kubelet stopped posting node status"); last heartbeat 07:03:08 KST, VM killed 07:06:41 KST.
On-chain feeds stale: baobab Feeds BTC-USDT, IDRX-USDT, KAIA-USDT stopped updating on-chain for ~174 minutes. (Baobab testnet only — cypress is unaffected because it runs on a separate host and, for the node service, GKE.)
Pods down: all orakl pods on k8s-node went Pending/Terminating for the full window (baobab schedules essentially the entire orakl workload onto this one worker).
Post-reboot crash cascade Off-chain node #1 — Vault sidecar not injected: after the node rebooted, 9 pods entered CrashLoopBackOff with /vault/secrets/config: No such file. Root cause: vault-agent-injector was itself down when those pods were recreated, so its mutating webhook never injected the vault-agent sidecar. Vault itself was healthy (sealed: false). Fixed by a rollout restart of the affected deployments once the injector was back.
Post-reboot crash cascade Adapter #2 — Loki compactor:loki-0 crashlooped with init compactor: failed to init delete store: timeout. The unclean shutdown corrupted its delete_requests boltdb. Renaming the directory let Loki recreate it.
Root cause
Host memory overcommit.server-1 has 67.4 GiB physical RAM but the VMs reserve ~72 GiB before the host OS is even counted:
VM
RAM
k8s-node
32 GiB
orakl-baobab-postgresql
13 GiB
k8s-ctr
9 GiB
baobab-vault
9 GiB
k8s-nfs
9 GiB
VM total
~72 GiB
host OS overhead
~4–6 GiB
committed
~76–78 GiB vs 67.4 GiB physical
That is ~4.6 GiB over-committed by the VMs alone, and ~9–11 GiB over once the host itself is included. When the host exhausted RAM the kernel OOM-killer selected the largest-RSS process, which is the 32 GiB k8s-node guest:
anon-rss: 32,211,644 kB ≈ 30.7 GiB, matching the 32 GiB k8s-node VM. constraint=CONSTRAINT_NONE + global_oom confirms this was host-wide RAM exhaustion, not a cgroup limit. The OOM score is roughly proportional to RSS, so a VM sized at 32 GiB is structurally the most likely victim on an overcommitted host — regardless of whether that VM is the actual cause of the pressure.
Contributing factors
1. Is the k8s-node VM's 32 GiB justified by the in-guest workload? Evidence is mixed — flagging the tension explicitly.
Sum of pod memory requests = 4.96 GiB — only 15.9% of allocatable. Requests are the only thing the scheduler reserves; by that measure the VM is enormously oversized.
Sum of pod memory limits = 17.97 GiB — 57.5% of allocatable. Limits (not requests) drive worst-case RSS, and even the theoretical everything-balloons-at-once ceiling is ~13 GiB below what the guest kernel sees (31.24 GiB) and ~14 GiB below the 32 GiB VM allocation.
But 31 of 45 pods have no memory limit on any container (34 of 45 have ≥1 uncapped container). Those pods are unbounded — real usage is not captured by the 17.97 GiB limit sum.
We could not measure actual in-guest usage: metrics-server is NOT installed on baobab. Both kubectl top pods -A and kubectl top nodes return error: Metrics API not available. All baobab figures above are declared requests/limits, not live RSS.
Tension worth calling out: the declared specs (17.97 GiB total limits, 4.96 GiB requests) do not justify a 32 GiB VM — yet the host OOM log shows the guest had actually touched ~30.7 GiB anon-rss. Those are not contradictory once you account for (a) guest kernel page cache, which the guest fills with free RAM and which appears to the host as guest anon-rss, (b) the 31 uncapped pods, and (c) KVM guest RSS ratcheting up to the high-water mark and rarely being reclaimed without ballooning. Without in-guest metrics we cannot attribute that 30.7 GiB between committed pod RSS, kernel page cache, and non-reclaimed guest memory. So "the VM is oversized" is supported by the declared workload but not yet proven against real steady-state usage — see Open Questions.
2. Monitoring stack is a large and mostly-uncapped footprint on the same worker.
The single biggest concentrated balloon risk on baobab is monitoring/loki-chunks-cache-0 (memcached): limit 8 GiB but request only 0.5 GiB — a 16x request-to-limit gap and the only container on the baobab node with a ≥4 GiB limit. Everything else is capped ≤2.06 GiB or uncapped.
loki-0, prometheus, grafana, promtail all have empty resources: {} on both clusters — no request and no limit — so the largest potential RAM consumers are uncapped and undercount the "total limits" exposure.
Monitoring accounts for ~51% of all capped memory limits on the worker on both clusters. On cypress (where metrics ARE available) the same loki chunks-cache is live at 8,253 MiB (~84% of its 9.6 GiB cap) — confirming this component genuinely consumes multiple GiB, not just reserves them.
3. The known orakl-node leak did NOT cause this, and a pod limit only partially caps guest pressure.
No OOMKilled containers anywhere on baobab. The pod-level orakl-node leak is real but self-heals: on cypress, orakl-node was cgroup-OOMKilled once on 2026-06-11 (restartCount=1) and orakl-vrf once on 2026-05-19, each recovering on restart. The most recent is ~29 days before this incident. This incident was a host-level global OOM, a different failure mode.
baobab orakl-node deployment: single container, limit 2 GiB / request 1 GiB (Burstable), no ts-sidecar. cypress: limit 4 GiB / request 1 GiB, plus a ts-sidecar with resources: {} (unbounded).
Does the pod memory limit cap guest RSS? Only partially. The limit does cgroup-OOM the leaking container and self-heal it, but it does not cap node/VM pressure because (a) unbounded sidecars/pods have no ceiling, and (b) on Burstable clusters requests ≪ limits, so several pods bursting toward their limits can exceed node RAM and drive node-level pressure a single pod's limit cannot prevent.
Secondary failures exposed by the outage
These are latent problems the reboot merely revealed; both deserve separate hardening tickets:
Vault-agent-injector webhook ordering. When vault-agent-injector is down, pods that (re)schedule are admitted without the vault-agent sidecar and silently start missing /vault/secrets/*, then CrashLoop. Nothing blocks or retries the injection. 9 pods hit this. Hardening options: make the injector's MutatingWebhookConfigurationfailurePolicy: Fail for the affected namespaces (so pods are rejected rather than admitted secret-less), and/or gate orakl deployments on injector readiness. Trade-off to weigh:failurePolicy: Fail can itself block scheduling during an injector outage — needs discussion.
Loki compactor boltdb corruption on unclean shutdown.loki-0 could not recover its delete_requests store (init compactor: failed to init delete store: timeout) after the hard power loss. Recovery required manual directory rename. Consider moving the compactor/delete store to more durable storage or adding a startup repair/reset path.
Proposed actions
(a) Immediate — relieve server-1 overcommit
Migrate one VM off server-1. Strongest candidate: orakl-baobab-postgresql (13 GiB); alternative baobab-vault (9 GiB). Live migration is preferred if the IP can remain static. — owner: @martin / infra team — effort: M (½–1 day incl. validation)
Confirm post-migration headroom: after moving a VM, host committed RAM should sit comfortably below 67.4 GiB with room for host OS + page cache. — owner: @martin — effort: S
(b) Short-term — right-size and cap
Right-size the k8s-node VM once true steady-state RSS is known (see Open Questions). Declared limits sum to 17.97 GiB; a 20–24 GiB VM may be sufficient and would remove the host-level overcommit even without a migration. — owner: @tom.cheese — effort: M
Set memory limits on the 31 uncapped baobab pods (34 uncapped containers), prioritizing loki, prometheus, grafana, promtail, and the orakl TS microservices. Right-size loki-chunks-cache (8 GiB limit vs 0.5 GiB request). — owner: @tom.cheese — effort: M
Install metrics-server on baobab so live usage is observable (it is already present on cypress). — owner: infra team — effort: S
(c) Preventive
Host-level memory alerting on all Proxmox hosts: alert when committed VM reservations approach physical RAM and when host MemAvailable drops below a threshold — so overcommit is caught before the OOM-killer runs. — owner: @martin — effort: M
Review host overcommit / KSM settings on server-1 (and disable or bound aggressive memory overcommit) so VM reservations cannot silently exceed physical RAM. — owner: infra team — effort: S–M
Audit cypress for the same latent risk. cypress is not hard-overcommitted on paper (worker limits 21.17 GiB < 31.24 GiB allocatable; live usage 16.47 GiB), but 32 of 44 worker pods have no memory limit, and its host carries k8s-node (32G) + k8s-ctr (9G) + ... too. Verify the cypress host's physical RAM vs VM reservations. — owner: @martin — effort: S
File the two secondary-failure hardening tickets (vault injector ordering, Loki boltdb). — owner: @tom.cheese — effort: S
Open questions
Migrate Postgres or Vault?orakl-baobab-postgresql (13 GiB) frees the most RAM; baobab-vault (9 GiB) is smaller but Vault's outage during this incident amplified the blast radius (sidecar injection failures). Which do we move, and does moving Vault off this host actually reduce coupling?
Can orakl-baobab-postgresql live-migrate while keeping a static IP? Infra flagged live migration as viable "if the IP can stay static" — needs confirmation for the DB specifically.
What is the true steady-state RSS of the k8s-node guest? The host saw ~30.7 GiB anon-rss but declared pod limits total only 17.97 GiB and requests 4.96 GiB. Until metrics-server is installed on baobab we cannot split that 30.7 GiB between committed pod memory, guest page cache, and non-reclaimed balloon — and therefore cannot finalize a right-sized VM value.
Do we want a broader memory-management pass? Infra's position is "we should do overall memory management" across both clusters — is that in scope for this ticket or a follow-up epic?
Summary
On 2026-07-10 07:06 KST the Proxmox host
server-1ran out of physical RAM and the Linux kernel OOM-killer terminated the KVM guest process for the baobabk8s-nodeworker VM (the largest-RSS process on the box). The single k8s worker node backing our baobab cluster disappeared, and every orakl pod scheduled there went Pending/Terminating for ~2h55m. This is an infrastructure-layer (Proxmox host memory overcommit) problem, not an orakl application bug: the sum of VM memory reservations onserver-1(~72 GiB) exceeds the host's physical RAM (67.4 GiB), so the host had no headroom and killed a whole guest. It is distinct from the long-standing pod-level orakl-node memory leak, which self-heals via container restart and did not fire here.Impact
k8s-nodewentNotReady("Kubelet stopped posting node status"); last heartbeat 07:03:08 KST, VM killed 07:06:41 KST.k8s-nodewent Pending/Terminating for the full window (baobab schedules essentially the entire orakl workload onto this one worker).CrashLoopBackOffwith/vault/secrets/config: No such file. Root cause:vault-agent-injectorwas itself down when those pods were recreated, so its mutating webhook never injected the vault-agent sidecar. Vault itself was healthy (sealed: false). Fixed by arollout restartof the affected deployments once the injector was back.loki-0crashlooped withinit compactor: failed to init delete store: timeout. The unclean shutdown corrupted itsdelete_requestsboltdb. Renaming the directory let Loki recreate it.Root cause
Host memory overcommit.
server-1has 67.4 GiB physical RAM but the VMs reserve ~72 GiB before the host OS is even counted:That is ~4.6 GiB over-committed by the VMs alone, and ~9–11 GiB over once the host itself is included. When the host exhausted RAM the kernel OOM-killer selected the largest-RSS process, which is the 32 GiB
k8s-nodeguest:anon-rss: 32,211,644 kB ≈ 30.7 GiB, matching the 32 GiB k8s-node VM.constraint=CONSTRAINT_NONE+global_oomconfirms this was host-wide RAM exhaustion, not a cgroup limit. The OOM score is roughly proportional to RSS, so a VM sized at 32 GiB is structurally the most likely victim on an overcommitted host — regardless of whether that VM is the actual cause of the pressure.Contributing factors
1. Is the k8s-node VM's 32 GiB justified by the in-guest workload? Evidence is mixed — flagging the tension explicitly.
From the baobab cluster audit (
orakl-baobab-admin@bisonai, nodek8s-node, capacity 32,860,460Ki = 31.34 GiB, allocatable 32,758,060Ki = 31.24 GiB, 45 pods / 41 Running):kubectl top pods -Aandkubectl top nodesreturnerror: Metrics API not available. All baobab figures above are declared requests/limits, not live RSS.Tension worth calling out: the declared specs (17.97 GiB total limits, 4.96 GiB requests) do not justify a 32 GiB VM — yet the host OOM log shows the guest had actually touched ~30.7 GiB anon-rss. Those are not contradictory once you account for (a) guest kernel page cache, which the guest fills with free RAM and which appears to the host as guest anon-rss, (b) the 31 uncapped pods, and (c) KVM guest RSS ratcheting up to the high-water mark and rarely being reclaimed without ballooning. Without in-guest metrics we cannot attribute that 30.7 GiB between committed pod RSS, kernel page cache, and non-reclaimed guest memory. So "the VM is oversized" is supported by the declared workload but not yet proven against real steady-state usage — see Open Questions.
2. Monitoring stack is a large and mostly-uncapped footprint on the same worker.
monitoring/loki-chunks-cache-0(memcached): limit 8 GiB but request only 0.5 GiB — a 16x request-to-limit gap and the only container on the baobab node with a ≥4 GiB limit. Everything else is capped ≤2.06 GiB or uncapped.loki-0,prometheus,grafana,promtailall have emptyresources: {}on both clusters — no request and no limit — so the largest potential RAM consumers are uncapped and undercount the "total limits" exposure.3. The known orakl-node leak did NOT cause this, and a pod limit only partially caps guest pressure.
orakl-nodewas cgroup-OOMKilled once on 2026-06-11 (restartCount=1) andorakl-vrfonce on 2026-05-19, each recovering on restart. The most recent is ~29 days before this incident. This incident was a host-level global OOM, a different failure mode.orakl-nodedeployment: single container, limit 2 GiB / request 1 GiB (Burstable), no ts-sidecar. cypress: limit 4 GiB / request 1 GiB, plus ats-sidecarwithresources: {}(unbounded).Secondary failures exposed by the outage
These are latent problems the reboot merely revealed; both deserve separate hardening tickets:
vault-agent-injectoris down, pods that (re)schedule are admitted without the vault-agent sidecar and silently start missing/vault/secrets/*, then CrashLoop. Nothing blocks or retries the injection. 9 pods hit this. Hardening options: make the injector'sMutatingWebhookConfigurationfailurePolicy: Failfor the affected namespaces (so pods are rejected rather than admitted secret-less), and/or gate orakl deployments on injector readiness. Trade-off to weigh:failurePolicy: Failcan itself block scheduling during an injector outage — needs discussion.loki-0could not recover itsdelete_requestsstore (init compactor: failed to init delete store: timeout) after the hard power loss. Recovery required manual directory rename. Consider moving the compactor/delete store to more durable storage or adding a startup repair/reset path.Proposed actions
(a) Immediate — relieve
server-1overcommitserver-1. Strongest candidate:orakl-baobab-postgresql(13 GiB); alternativebaobab-vault(9 GiB). Live migration is preferred if the IP can remain static. — owner:@martin/ infra team — effort: M (½–1 day incl. validation)@martin— effort: S(b) Short-term — right-size and cap
k8s-nodeVM once true steady-state RSS is known (see Open Questions). Declared limits sum to 17.97 GiB; a 20–24 GiB VM may be sufficient and would remove the host-level overcommit even without a migration. — owner:@tom.cheese— effort: Mloki,prometheus,grafana,promtail, and the orakl TS microservices. Right-sizeloki-chunks-cache(8 GiB limit vs 0.5 GiB request). — owner:@tom.cheese— effort: M(c) Preventive
MemAvailabledrops below a threshold — so overcommit is caught before the OOM-killer runs. — owner:@martin— effort: Mserver-1(and disable or bound aggressive memory overcommit) so VM reservations cannot silently exceed physical RAM. — owner: infra team — effort: S–Mk8s-node (32G) + k8s-ctr (9G) + ...too. Verify the cypress host's physical RAM vs VM reservations. — owner:@martin— effort: S@tom.cheese— effort: SOpen questions
orakl-baobab-postgresql(13 GiB) frees the most RAM;baobab-vault(9 GiB) is smaller but Vault's outage during this incident amplified the blast radius (sidecar injection failures). Which do we move, and does moving Vault off this host actually reduce coupling?orakl-baobab-postgresqllive-migrate while keeping a static IP? Infra flagged live migration as viable "if the IP can stay static" — needs confirmation for the DB specifically.k8s-nodeguest? The host saw ~30.7 GiB anon-rss but declared pod limits total only 17.97 GiB and requests 4.96 GiB. Until metrics-server is installed on baobab we cannot split that 30.7 GiB between committed pod memory, guest page cache, and non-reclaimed balloon — and therefore cannot finalize a right-sized VM value.