# How celld Handles Deployment Rollbacks Using Versioned Manifests

> celld enables atomic deployment rollbacks with versioned manifests stored in object storage. Revert instantly to older code and SQLite state without data loss.

- Repository: [Deno/celld](https://github.com/denoland/celld)
- Tags: architecture
- Published: 2026-08-09

---

**celld implements atomic deployment rollbacks by storing immutable versioned manifests in object storage and moving a `current` pointer to a previous version, enabling instant reversion to older code and SQLite state without data loss.**

The denoland/celld project provides a durable compute platform that treats every deployment as an immutable artifact. Understanding how celld deployment rollbacks work requires examining its versioned manifest architecture, where each release is stored as an immutable JSON document and a single pointer determines which version nodes execute.

## Immutable Versioned Manifest Storage

Each deployment in celld is stored as an immutable, version-qualified manifest in shared object storage. According to the protocol definition in [`crates/celld/protocol.rs`](https://github.com/denoland/celld/blob/main/crates/celld/protocol.rs) (lines 10-15), manifests are written to the path:

```

deploy/<script>/<version>/manifest.json

```

This immutability guarantee ensures that historical deployment artifacts remain intact indefinitely. The manifest contains the complete deployment specification, including module lists, static assets, and durable object class definitions. Because the file is never modified after creation, rolling back to any previous version simply requires referencing its existing manifest path.

## The Current Pointer and Atomic Compare-and-Swap

Rather than overwriting deployment data, celld maintains a `current` pointer in the object storage bucket that references the active version's manifest. When a new deployment succeeds, celld updates this pointer to reference the new version's manifest path, a process handled in [`crates/celld/deploy.rs`](https://github.com/denoland/celld/blob/main/crates/celld/deploy.rs).

A rollback operation performs an atomic **compare-and-swap** on this pointer, moving it from the current version to a previous version's manifest path. This atomic operation guarantees that all nodes observe the same version simultaneously, eliminating race conditions during rollback events. The pointer update is transactional—either all nodes see the new (old) version, or none do.

## Storage Layer Rollback Handling

The storage layer treats rollback operations as first-class transactional actions. In [`crates/celld/storage.rs`](https://github.com/denoland/celld/blob/main/crates/celld/storage.rs) (lines 1132-1134), the action handling logic specifically recognizes the `"rollback"` string as a valid operation, returning `Ok(None)` upon successful completion:

```rust
// Storage action handling in storage.rs
match action {
    "rollback" => Ok(None),
    // ... other actions like commit
}

```

This implementation ensures that pointer rewinds are handled consistently with other storage transactions, maintaining data integrity during version reversion operations.

## Runtime Manifest Loading and State Restoration

When celld nodes start up, they read the manifest referenced by the `current` pointer to configure their runtime environment. The fleet initialization code in [`crates/celld/fleet.rs`](https://github.com/denoland/celld/blob/main/crates/celld/fleet.rs) (lines 371-374) deserializes this manifest:

```rust
// Fleet initialization loads the current manifest
let manifest = load_manifest_from_pointer(&current_pointer)
    .await
    .expect("Failed to load deployment manifest");

```

If the pointer references an older version due to a rollback, the node automatically fetches the corresponding manifest and restores the associated SQLite snapshots from object storage. This process requires no manual intervention—nodes detect the pointer change, load the previous code modules, and resume execution with the historical state data.

## Implementing a Rollback in celld

You can trigger a rollback programmatically using the storage API or via the CLI. Both methods utilize the same atomic compare-and-swap mechanism implemented in the storage layer.

### Programmatic Rollback

To rollback programmatically, construct a pointer to the target version and perform an atomic swap:

```rust
use celld::storage::Bucket;
use celld::protocol::Pointer;

// Initialize bucket connection
let bucket = Bucket::new("s3://my-cells-bucket");

// Create pointer referencing version 3 manifest
let rollback_pointer = Pointer {
    prefix: "deploy/my-worker/3".to_string(),
    // additional fields omitted for brevity
};

// Atomically update current pointer
bucket.compare_and_swap_pointer("current", &rollback_pointer)?
    .expect("Rollback completed successfully");

```

### CLI-Based Rollback

The command-line interface defined in [`crates/celld/main.rs`](https://github.com/denoland/celld/blob/main/crates/celld/main.rs) provides a convenience flag for rollbacks:

```bash
celld deploy . \
  --bucket s3://my-cells-bucket \
  --rollback-version 3

```

This command internally constructs the appropriate pointer path and executes the compare-and-swap operation against the `current` pointer.

## Summary

- **Immutable manifests**: Every deployment is stored permanently at `deploy/<script>/<version>/manifest.json`, ensuring historical versions remain accessible for rollbacks.
- **Pointer indirection**: A `current` pointer in object storage determines which version nodes execute, enabling instant version switching without data migration.
- **Atomic updates**: Rollbacks use compare-and-swap operations to guarantee consistent version visibility across all nodes in the fleet.
- **Automatic state restoration**: Nodes automatically load SQLite snapshots associated with the target version when processing a rollback.
- **Transactional safety**: The storage layer treats `"rollback"` as a native action type, ensuring operational consistency with other storage transactions.

## Frequently Asked Questions

### How does celld ensure data consistency during a rollback?

celld ensures consistency through immutable manifests and atomic pointer updates. Because manifests in [`crates/celld/protocol.rs`](https://github.com/denoland/celld/blob/main/crates/celld/protocol.rs) are never modified after creation, and the `current` pointer update uses a compare-and-swap operation, all nodes transition to the previous version simultaneously without risk of split-brain scenarios or partial deployments.

### What happens to SQLite data when rolling back to a previous version?

When the `current` pointer moves to an older version, nodes automatically restore the SQLite snapshots associated with that version's manifest. These snapshots are stored alongside the manifests in object storage, allowing the runtime in [`crates/celld/fleet.rs`](https://github.com/denoland/celld/blob/main/crates/celld/fleet.rs) to reconstruct the exact database state that existed during the target deployment.

### Can rollbacks be performed to any previous version?

Yes. Since celld stores every deployment as an immutable artifact at `deploy/<script>/<version>/manifest.json`, you can rollback to any existing version by updating the `current` pointer to reference that specific version path. There is no limit on how far back you can roll, provided the manifest and its associated snapshots remain in storage.

### Is the rollback operation reversible?

Yes. Because celld never deletes or modifies versioned manifests, you can roll forward again by moving the `current` pointer back to a newer version. This effectively acts as a "roll-forward" operation, restoring the system to the more recent deployment state using the same atomic pointer mechanism.