e2b-dev / e2b-dev/runtime

fix(placement,fc): memory-unaware scheduling risks host OOM; balloon DeflateOnOom should be true

Open
#3,379 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
1.6k
Forks
438
PR merge metrics
No merged PRs in 30d

Description

Background

A sandbox created with ram_mb=2048 gets Firecracker MemSizeMib=2048 — an absolute hard ceiling imposed by the VMM. The guest kernel sees exactly that much physical RAM and cannot exceed it; when it tries, the in-guest OOM killer fires. There are three compounding gaps that make this worse than necessary.


Gap 1 — Placement ignores memory (host OOM risk)

The BestOfK scoring function and its sample() filter are purely CPU-aware:

// placement_best_of_K.go
totalCapacity := config.R * cpuCount          // only CPU
return (cpuRequested + float64(reserved) + config.Alpha*usageAvg) / totalCapacity

MemoryAllocatedBytes and MemoryTotalBytes are tracked per node but never used in placement decisions. A node with 64 GiB RAM can receive unlimited 2 GiB sandboxes until the host kernel OOMs and kills Firecracker processes indiscriminately.

CPU has an explicit overcommit ratio (BestOfKMaxOvercommit, default 4×). Memory has no equivalent guard.

Fix: add a BestOfKMaxMemoryOvercommit feature flag (default 0 = disabled) and:

  • Hard filter in sample(): skip nodes where MemoryAllocatedBytes + requested > M × MemoryTotalBytes
  • Soft term in Score(): prefer nodes with available memory headroom

Gap 2 — Balloon DeflateOnOom=false causes avoidable in-guest OOMs

The virtio-balloon device is installed with:

// fc/client.go
deflateOnOom := false

Free-page hinting/reporting is enabled (good), but when the guest approaches its memory ceiling, the balloon is not automatically deflated to return previously reclaimed pages to the guest. A guest that has had idle pages harvested via FPH will OOM-kill user processes even though the host could give those pages back instantly by deflating the balloon.

Fix: set DeflateOnOom = true.


Gap 3 — Host cgroup has no memory limit (defense-in-depth gap)

Each Firecracker process runs in a per-sandbox cgroup at /sys/fs/cgroup/e2b/sbx-{id}. The cgroup correctly enables the memory controller and reads memory.current / memory.peak for observability, but never writes memory.max or memory.high:

// cgroup/manager.go — Create() only opens the directory and memory.peak FD
// memory.max is never set

If Firecracker itself has a bug that causes it to allocate beyond the guest's MemSizeMib on the host side, there is no second enforcement layer.

Fix (follow-up): write memory.high = MemSizeMib × 1.05 to the sandbox cgroup at creation time as a soft backstop.


Proposed changes (this issue tracks all three)

# Change File(s) Complexity
1 Memory-aware placement filter + score placement_best_of_K.go, orchestrator.go, flags.go Low
2 DeflateOnOom = true fc/client.go Trivial
3 Cgroup memory.high enforcement cgroup/manager.go, sandbox creation Medium

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with placement_best_of_K.go, orchestrator.go, and flags.go to trace the existing CPU filter, scoring, and feature-flag paths. Then inspect fc/client.go and cgroup/manager.go alongside sandbox creation to understand the balloon and cgroup setup. Done means memory-aware placement, DeflateOnOom enabled, and the proposed memory.high backstop are implemented across the named areas.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, linux
Domain
infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.