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.