Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/denoland/celld/llms.txt

Use this file to discover all available pages before exploring further.

In this quickstart you will install celld, connect it to an S3-compatible bucket, deploy the counter Durable Object example, and send your first request to a running cell — all in five steps. The counter example is a SQLite-backed Durable Object that increments a persistent counter on every request. It is the simplest possible demonstration of cell state surviving across requests and node restarts.
Worker projects deployed with celld deploy require esbuild on your PATH. Install it before running the deploy step.
1

Install celld

Run the one-line installer to download the celld binary:
curl -fsSL https://celld.dev/install.sh | sh
If the installer asks you to, add ~/.local/bin to your PATH. Verify the installation:
celld --version
To pin a specific release, set CELLD_VERSION to the desired tag before running the installer:
CELLD_VERSION=v0.0.1 curl -fsSL https://celld.dev/install.sh | sh
You can verify the provenance of any downloaded binary with:
gh attestation verify <asset> --repo denoland/celld
2

Configure object storage credentials

celld uses the standard AWS credential chain for S3-compatible stores. For Cloudflare R2, create a bucket and an S3 API token with access to that bucket, then export these variables:
export AWS_ACCESS_KEY_ID=your-access-key-id
export AWS_SECRET_ACCESS_KEY=your-secret-access-key
export AWS_REGION=auto
export S3_ENDPOINT=https://ACCOUNT_ID.r2.cloudflarestorage.com
export CELLD_BUCKET=s3://your-bucket-name
For Google Cloud Storage, authenticate with Application Default Credentials and set the bucket using a gs:// prefix:
gcloud auth application-default login
export CELLD_BUCKET=gs://your-bucket-name
A gs:// bucket does not use S3_ENDPOINT or AWS_* credentials.For Amazon S3, use the standard AWS credential chain and omit --endpoint and --region from celld commands — they will be inferred automatically.
3

Deploy the counter example

Clone the celld repository and deploy the counter example to your bucket:
git clone https://github.com/denoland/celld
cd celld/examples/counter
celld deploy . \
  --bucket "$CELLD_BUCKET" \
  --endpoint "$S3_ENDPOINT" \
  --region "$AWS_REGION"
The counter example consists of two files. Here is the full Worker code:
// examples/counter/index.js
export class Counter {
  constructor(state, env) { this.state = state; }
  async fetch(request) {
    let n = (await this.state.storage.get("n")) ?? 0;
    n++;
    await this.state.storage.put("n", n);
    return new Response(JSON.stringify({ n, url: request.url }), { status: 200 });
  }
}
export default {
  async fetch(request, env) {
    const id = env.COUNTER.idFromName("room-42");
    return env.COUNTER.get(id).fetch(request);
  }
};
And the Wrangler configuration:
// examples/counter/wrangler.jsonc
{
  "name": "counter",
  "main": "index.js",
  "compatibility_date": "2026-01-01",
  "durable_objects": { "bindings": [{ "name": "COUNTER", "class_name": "Counter" }] },
  "migrations": [{ "tag": "v1", "new_sqlite_classes": ["Counter"] }]
}
celld deploy invokes esbuild from your PATH to bundle the Worker code, then writes the deployment and any static assets directly to the bucket. Every node in the fleet picks up the latest deployment automatically.
4

Start a node

Start celld and point it at the same bucket:
celld \
  --bucket "$CELLD_BUCKET" \
  --endpoint "$S3_ENDPOINT" \
  --region "$AWS_REGION"
For local development the default listener (0.0.0.0:8080) is fine. The node loads the deployment you pushed in the previous step and begins accepting traffic.
When you are ready to run multiple nodes in production, see the fleet operations guide. Each additional node just needs --internal-listen and --advertise set to an address the other nodes can reach — there is no join command.
5

Send a request

With the node running, send an HTTP request to the Worker:
curl http://localhost:8080/
You should see a JSON response like:
{"n":1,"url":"http://localhost:8080/"}
Each subsequent request increments the counter. The value is durable: stop the node, restart it, and the counter picks up where it left off because the SQLite database is continuously replicated to your bucket.
curl http://localhost:8080/
# {"n":2,"url":"http://localhost:8080/"}

curl http://localhost:8080/
# {"n":3,"url":"http://localhost:8080/"}

Next steps

  • Explore the other examples — WebSocket echo, alarms, JS RPC, and more.
  • Read the Installation guide for Docker and build-from-source options.
  • Review the limitations before deploying to production.

Build docs developers (and LLMs) love