CELLD_MAX_RSS_MB vs CELLD_MAX_RESIDENT_CELLS: Understanding CellD Memory Limits

CELLD_MAX_RSS_MB limits the total process memory in megabytes by triggering memory-pressure shedding, while CELLD_MAX_RESIDENT_CELLS limits the count of active cells held in memory, refusing new loads when the cap is reached.

Both environment variables control independent resource-limiting mechanisms in the celld server from the denoland/celld repository. Understanding the difference between CELLD_MAX_RSS_MB and CELLD_MAX_RESIDENT_CELLS helps operators tune cell capacity without risking out-of-memory kills or excessive swapping.

What CELLD_MAX_RSS_MB Controls

CELLD_MAX_RSS_MB sets a threshold on the resident-set size (RSS)—the total physical memory the operating system attributes to the celld process.

How RSS Monitoring Works

By default, celld enforces a threshold of approximately 80% of the machine's available RAM. Setting CELLD_MAX_RSS_MB overrides this default:

  • Value in MB: Sets explicit memory ceiling
  • 0: Disables RSS-based pressure shedding entirely
  • Behavior when exceeded: The node begins shedding cells to reduce memory footprint

The implementation lives in crates/celld/memory.rs, where the RSS monitor polls process statistics and triggers shedding when usage crosses the configured threshold.


# Cap memory usage at 2 GB

CELLD_MAX_RSS_MB=2048 \
celld --bucket s3://my-cells-bucket \
      --listen 0.0.0.0:8080 \
      --internal-listen 10.0.0.12:8081 \
      --advertise node-a.internal:8081

Important caveat: Shedding cells reduces active memory pressure but may not immediately lower reported RSS, as the allocator often retains freed pages for reuse.

What CELLD_MAX_RESIDENT_CELLS Controls

CELLD_MAX_RESIDENT_CELLS caps the number of cells held in memory, regardless of their individual memory footprints.

How Resident Cell Limits Work

Each loaded cell corresponds to an in-memory SQLite database. When the limit is reached:

  • New cell loads are refused until existing cells are evicted
  • Eviction happens via pressure shedding, idle timeout, or explicit close
  • The limit operates independently of RSS—a node can stay under the memory cap while being throttled by the cell count cap

Check the enforcement logic in crates/celld/cell.rs, where the load-decision code validates capacity before admitting a cell to residency.


# Allow at most 1,000 concurrent resident cells

CELLD_MAX_RESIDENT_CELLS=1000 \
celld --bucket s3://my-cells-bucket \
      --listen 0.0.0.0:8080 \
      --internal-listen 10.0.0.12:8081 \
      --advertise node-a.internal:8081

Key Differences Between CELLD_MAX_RSS_MB and CELLD_MAX_RESIDENT_CELLS

Aspect CELLD_MAX_RSS_MB CELLD_MAX_RESIDENT_CELLS
What it limits Process memory size (megabytes) Count of resident cells
Mechanism Memory-pressure shedding Load refusal / admission control
Granularity Coarse (total process RSS) Fine (individual cells)
Failure mode Active shedding of loaded cells Request blocking until capacity frees
Use case Prevent OOM kills from OS Bound concurrency regardless of cell size

When to Use Each Limit

Use CELLD_MAX_RSS_MB when your primary concern is the overall memory footprint of the celld process—especially in shared or containerized environments where cgroup limits apply.

Use CELLD_MAX_RESIDENT_CELLS when you need predictable concurrency bounds or when individual cells vary dramatically in size. A single large cell might consume hundreds of megabytes, making a pure count-based limit safer for capacity planning.

Combine both for defense in depth: CELLD_MAX_RESIDENT_CELLS provides a hard cap on active connections, while CELLD_MAX_RSS_MB acts as a safety net against unexpected memory growth from large cells or internal overhead.

Source Code References

According to the denoland/celld source code:

  • crates/celld/memory.rs: Implements RSS polling and shedding triggers for CELLD_MAX_RSS_MB
  • crates/celld/cell.rs: Enforces CELLD_MAX_RESIDENT_CELLS during cell load operations
  • README.md: Documents both variables under the "Memory pressure shedding" and "Resource limits" sections

Summary

  • CELLD_MAX_RSS_MB limits total process memory via shedding; measured in megabytes; located in memory.rs
  • CELLD_MAX_RESIDENT_CELLS limits active cell count via admission control; unitless count; enforced in cell.rs
  • Both are independent—configure together for robust resource management
  • Set to 0 to disable RSS-based shedding (not recommended for production)

Frequently Asked Questions

Can I set both CELLD_MAX_RSS_MB and CELLD_MAX_RESIDENT_CELLS at the same time?

Yes. The two limits operate independently and complement each other. CELLD_MAX_RESIDENT_CELLS prevents unbounded cell loading, while CELLD_MAX_RSS_MB catches memory growth from large cells or internal fragmentation that the count limit cannot predict.

Why doesn't RSS drop immediately after shedding cells?

The memory allocator (typically the system allocator or jemalloc) retains freed pages for reuse rather than returning them to the OS immediately. This is normal behavior—RSS may plateau while the resident cell count falls. The shedding mechanism still prevents new allocations from growing beyond your configured threshold.

What happens if I set CELLD_MAX_RSS_MB to 0?

Setting CELLD_MAX_RSS_MB=0 disables the RSS-based pressure-shedding mechanism entirely. Without this safety net, celld relies solely on CELLD_MAX_RESIDENT_CELLS and idle timeouts to manage memory. Running without RSS protection risks OOM kills if cells grow larger than expected or internal overhead increases.

Which limit should I tune first for a new deployment?

Start with CELLD_MAX_RSS_MB set to 70–80% of your container or host memory limit to prevent crashes. Then add CELLD_MAX_RESIDENT_CELLS based on observed memory per cell in your workload—divide your RSS target by typical cell size to derive a safe count cap.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →