Documentation

Deployment

Production deployment profiles for celld on Coolify with Cloudflare and R2.

The examples below use the current Xicar baseline image:

ghcr.io/denoland/celld:v0.6.0

Pin a release tag in production. Do not follow latest automatically.

Single-node production

Use the single-node profile for lower-write celld applications where a bucket round trip on durable writes is acceptable.

The node is intentionally disposable. No persistent volume is required because every acknowledged write uses the bucket as its durability proof.

CELLD_BUCKET=s3://xicar-celld-<app>
S3_ENDPOINT=https://<CLOUDFLARE_ACCOUNT_ID>.r2.cloudflarestorage.com
AWS_REGION=auto
AWS_ACCESS_KEY_ID=<R2_ACCESS_KEY_ID>
AWS_SECRET_ACCESS_KEY=<R2_SECRET_ACCESS_KEY>

CELLD_ADDR=0.0.0.0:8080
CELLD_INTERNAL_ADDR=127.0.0.1:8081
CELLD_TRUST_FORWARDED_HEADERS=1

CELLD_DURABILITY=bucket
CELLD_MAX_REQUEST_BODY_BYTES=16777216

RUST_LOG=info

Coolify should expose only port 8080. Keep the internal/operator listener on loopback for a single-node deployment.

Expected lifecycle:

container replaced
      ↓
local SQLite/cache disappears
      ↓
new celld process starts
      ↓
state restores or pages from R2

Multi-node fleet

Use the fleet profile when write latency, sustained write volume, or availability justifies multiple nodes.

Each node gets its own persistent local volume. The volumes are independent and are never shared or replicated by Coolify.

Server A                 Server B                 Server C
celld A                  celld B                  celld C
   │                        │                        │
local volume             local volume             local volume
/var/lib/celld           /var/lib/celld           /var/lib/celld
   └──────────────── celld replication ─────────────┘
                            │
                            ▼
                       shared R2

Common environment:

CELLD_BUCKET=s3://xicar-celld-<app>
S3_ENDPOINT=https://<CLOUDFLARE_ACCOUNT_ID>.r2.cloudflarestorage.com
AWS_REGION=auto
AWS_ACCESS_KEY_ID=<R2_ACCESS_KEY_ID>
AWS_SECRET_ACCESS_KEY=<R2_SECRET_ACCESS_KEY>

CELLD_ADDR=0.0.0.0:8080
CELLD_INTERNAL_ADDR=0.0.0.0:8081
CELLD_ADVERTISE=<PRIVATE_NODE_ADDRESS>:8081
CELLD_TRUST_FORWARDED_HEADERS=1

CELLD_WATCH=/var/lib/celld/state
CELLD_DURABILITY=fleet
CELLD_MAX_REQUEST_BODY_BYTES=16777216

RUST_LOG=info

Mount a persistent local volume at /var/lib/celld on every node. Do not use NFS or one shared filesystem for these directories.

The advertised peer address must be reachable only through the private server network or an encrypted overlay such as WireGuard or Tailscale.

Networking

Listener Purpose Exposure
8080 Worker/public traffic Cloudflare, load balancer, or trusted reverse proxy
8081 Peer protocol and operator API Private network only

celld does not terminate TLS. Cloudflare or the trusted ingress layer terminates HTTPS before forwarding HTTP to the public celld listener.

Only set CELLD_TRUST_FORWARDED_HEADERS=1 when the public listener is behind a trusted proxy that overwrites forwarded host and scheme headers.

Storage

Use a dedicated R2 bucket for each production fleet or application boundary. The bucket credentials effectively control the fleet, so scope them to only the required bucket.

Graduating from single node to fleet

The application code does not need a different Durable Objects design when it graduates.

1 node + bucket durability + disposable disk
                    ↓
2–3 nodes + fleet durability + local persistent disks

Use the graduation point when write latency, R2 Class A operation volume, sustained write throughput, maintenance availability, or faster recovery becomes material.

References