How celld Manages Idle Cell Hibernation to Optimize Resource Usage

celld optimizes resource usage by hibernating idle cells—copying their SQLite state to local .hibernated files and uploading them to S3-compatible storage—allowing the runtime to evict inactive cells from memory while preserving their state for fast wake-up.

Idle cell hibernation is the primary mechanism celld uses to balance memory constraints with the responsiveness of stateful serverless functions. When a cell (a lightweight V8 isolate with a dedicated SQLite database) stops receiving requests, the runtime can persist its state remotely and reclaim local resources. This article examines the implementation details found in the denoland/celld repository, covering the snapshot process, eviction triggers, and wake-up mechanics.

The Three-Phase Hibernation Process

Local Snapshot Creation

When a cell becomes eligible for hibernation, the runtime first creates a local backup. In crates/celld/ltx_repl.rs at line 310, the hibernate function copies the current SQLite database to a .hibernated file on the local node. This step ensures data durability even before remote upload completes and can optionally preserve the snapshot locally using the preserve_local flag for faster subsequent wake-ups.

Remote Persistence to Object Storage

The local snapshot is then uploaded to a configured S3-compatible bucket, transforming the active in-memory process into a static object that incurs minimal storage cost. As documented in the project's docs/README.md, this persistence model allows cells to exist as dormant objects rather than resident processes, dramatically reducing the memory footprint of the node.

In-Memory Eviction and Permit Tracking

Once the snapshot is safely stored, the runtime closes the SQLite connection and removes the cell from the resident set. The system increments a hibernation permit counter defined in crates/logic/lib.rs at line 3463, tracking how many cells are currently hibernated. This counter is bounded by the max_hibernations configuration parameter (defined at line 87 and enforced at line 1257 in the same file), preventing the node from overwhelming the bucket with excessive concurrent snapshots.

When Hibernation Occurs

Pressure-Based Eviction

The runtime monitors resource utilization through environment variables like CELLD_MAX_RSS_MB and CELLD_MAX_CPU_PERCENT. When memory or CPU thresholds are exceeded, the scheduler traverses resident cells and evicts the least-recently used candidates. Idle cells—those without active WebSocket connections or pending alarms—are prioritized for hibernation because they can withstand the wake-up latency without disrupting active client sessions.

Explicit Idle Detection

A cell is considered safely hibernatable when it has no active inbound WebSocket connections marked as non-hibernatable. The JavaScript harness in crates/celld/js/harness.js at line 1093 sets the ws._hibernatable flag to indicate that a connection can be safely dropped during hibernation. Cells with pending alarms or active non-hibernatable WebSockets remain resident to ensure timely execution.

The Wake-Up Flow

When a request arrives for a hibernated cell, the runtime executes a three-step restoration process:

  1. Bucket Lookup: The system queries the S3-compatible bucket for the latest cell snapshot.
  2. Local Restoration: The SQLite file is restored to the local filesystem. If a .hibernated copy was preserved locally (and is still valid), the runtime reuses it instead of downloading, as implemented in crates/celld/ltx_repl.rs lines 182-199.
  3. Activation: The cell is loaded back into memory, the SQLite connection is re-established, and the hibernation permit is released back to the pool.

This wake-up process is intentionally more expensive than standard activation, incentivizing the scheduler to keep frequently accessed cells resident.

Configuration and Implementation Examples

Configuring Hibernation Limits

Control the maximum number of concurrently hibernated cells via the CELLD_HIBERNATIONS environment variable:

// crates/celld/main.rs – configuring limits
let max_hibernations = positive_environment::<usize>("CELLD_HIBERNATIONS")?;
let limits = Limits {
    // … other limits …
    hibernations: max_hibernations,
    // …
};

Start the node with a custom limit:

export CELLD_HIBERNATIONS=500   # allow up to 500 concurrent hibernated cells

celld --bucket s3://my-bucket …

Triggering Hibernation Programmatically

The RuntimeManager exposes an async hibernate method that delegates to the LTX replication layer:

// RuntimeManager::hibernate – crates/celld/runtime.rs
async fn hibernate(&self, cell: &str, epoch: u64, preserve_local: bool) {
    self.ltx.hibernate(cell, epoch, preserve_local).await
}

The underlying implementation in crates/celld/ltx_repl.rs handles the file operations:

// crates/celld/ltx_repl.rs – line 310
pub async fn hibernate(&self, cell: &str, epoch: u64, preserve_local: bool) {
    let db = self.db_path(cell, epoch);
    let preserved = db.with_extension("hibernated");
    // Copies SQLite → .hibernated and uploads to bucket
}

Checking Cell Status via JavaScript

Worker scripts can query the hibernation state of any cell:

// In a Worker script
import { hibernationState } from "celld:control";

async function example(cellId) {
  const state = await hibernationState(cellId);
  console.log(`Cell ${cellId} is ${state ? "hibernated" : "resident"}`);
}

This API queries the Rust control plane, which checks the S3 bucket entry to determine if the cell exists in a hibernated state.

Summary

  • Idle cell hibernation in celld reduces memory usage by persisting cell state to S3-compatible storage and evicting inactive processes from RAM.
  • The process involves three distinct phases: local snapshot creation (.hibernated files), remote upload, and in-memory eviction tracked via permit counters.
  • Hibernation triggers include resource pressure (CPU/memory thresholds) and explicit idle detection when no non-hibernatable WebSockets or pending alarms exist.
  • Wake-up requires downloading or reusing the local snapshot, restoring the SQLite database, and re-activating the V8 isolate, incurring higher latency than standard activation.
  • The max_hibernations limit and CELLD_HIBERNATIONS environment variable prevent resource exhaustion on both the node and the backing object storage.

Frequently Asked Questions

What happens to active WebSocket connections when a cell hibernates?

WebSocket connections marked as hibernatable (via the ws._hibernatable flag in js/harness.js) are terminated during hibernation. The client must reconnect upon wake-up. Cells with active non-hibernatable connections are excluded from hibernation to prevent connection drops.

How does celld prevent data loss during hibernation?

Data durability is ensured through the two-phase persistence model. The SQLite database is first copied to a local .hibernated file before any upload begins. Only after successful upload to the S3-compatible bucket does the runtime close the database connection and evict the cell from memory.

Can I disable hibernation for specific cells?

While there is no per-cell hibernation flag in the current implementation, cells automatically opt-out of hibernation when they hold non-hibernatable WebSocket connections or have pending alarms scheduled. Setting max_hibernations to zero effectively disables hibernation node-wide.

What is the performance cost of waking up a hibernated cell?

Wake-up latency is significantly higher than cold-start or warm-start activation because it requires object storage retrieval and SQLite restoration. The system mitigates this by preferring to keep frequently accessed cells resident and by optionally preserving local .hibernated snapshots to bypass downloads when safe.

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 →