Cloudflare Durable Object storage
cloudflareDurableStorage() stores each CaretCMS site in a SQLite-backed
Cloudflare Durable Object. The object is the authority for entry data,
revisions, history, collection indexes, and collection metadata.
Use this adapter for a Cloudflare Workers site when multiple requests or editors
may save at the same time. It coordinates those writes so one save cannot silently
overwrite another save based on an older revision.
The legacy KV adapter cannot coordinate those writes. Core submits
a validated entry transition through the optional StorageAdapter.commitEntries
contract. The Durable Object compares every entry revision and existence state,
then commits the complete batch. A conflict changes nothing. Single-entry saves
and deletes update data, revision, history, and indexes together; a reorder
commits every participating entry together.
Install @caretcms/core@0.5.0, @caretcms/cloudflare@0.5.0,
@astrojs/cloudflare, and Wrangler in your Astro project. Configure production
authentication and an R2 public URL if using uploads.
Astro configuration
Section titled “Astro configuration”Use the Durable Object provider for content. R2 remains the upload store.
import { defineConfig } from "astro/config";import cloudflare from "@astrojs/cloudflare";import caret from "@caretcms/core";import { cloudflareDurableStorage, r2Uploads } from "@caretcms/cloudflare";
export default defineConfig({ output: "server", adapter: cloudflare(), integrations: [ caret({ storage: cloudflareDurableStorage({ binding: "CMS_CONTENT", instanceName: "production", }), uploads: r2Uploads({ binding: "CMS_R2" }), }), ],});Astro applications with custom Cloudflare exports need a Worker entrypoint.
import { handle } from "@astrojs/cloudflare/handler";export { CaretCmsContent } from "@caretcms/cloudflare/durable-object";
export default { fetch(request, env, ctx) { return handle(request, env, ctx); },};Point Wrangler at that entrypoint, bind the namespace, and declare the class as a SQLite-backed Durable Object. Cloudflare’s current configuration also supports legacy migrations for applications that already use them; do not mix the two lifecycle formats.
name = "my-caret-site"compatibility_date = "2026-09-13"compatibility_flags = ["nodejs_compat"]main = "./src/worker.js"
[[durable_objects.bindings]]name = "CMS_CONTENT"class_name = "CaretCmsContent"
[exports.CaretCmsContent]type = "durable-object"storage = "sqlite"
[[r2_buckets]]binding = "CMS_R2"bucket_name = "my-site-uploads"Create the R2 bucket with npx wrangler r2 bucket create my-site-uploads.
Set R2_PUBLIC_DOMAIN to its enabled public hostname (or pass publicBaseUrl
to r2Uploads). Use Worker secrets for the editor password and session secret.
Run astro build before Wrangler. Astro bundles the custom entrypoint and emits
dist/server/wrangler.json; deploy that generated configuration so Wrangler
uses Astro’s resolved virtual modules and asset paths.
npm run buildnpx wrangler deploy --config dist/server/wrangler.jsonSee Cloudflare’s documentation for Durable Object class lifecycle and Astro’s custom Cloudflare entrypoint.
Object boundaries and overlays
Section titled “Object boundaries and overlays”instanceName selects the site’s coordination object. Use a stable, unique name
per tenant or independently managed site. Caret creates separate object names
for each persistent editor draft and each temporary demo session, so unrelated
drafts do not serialize through the published-content object. Demo-session
objects refresh a two-hour alarm and erase their storage when it fires.
Bundled .caret/data remains a read-only baseline by default. The first edit
materializes the changed entry in the object; deletions use durable tombstones so
the build-bundled entry does not reappear. Set bundledFallback: false for an
empty object-backed site.
Moving from KV
Section titled “Moving from KV”The adapter does not copy existing runtime values from CloudflareKvStorageAdapter.
Changing providers keeps build-bundled seed content but requires an explicit
export/import plan for entries that exist only in KV.
Verify your deployment
Section titled “Verify your deployment”Check the generated Worker uses the intended account, namespace, class and R2 bucket. Exercise saving and history with test content, then verify that two same-revision writes produce one success and one conflict. Check draft/session isolation if those modes are enabled. Use a staging deployment for destructive tests.
The adapter coordinates entry transitions and reorder batches within an object. This does not turn a multi-entry publication into one global transaction; review individual publish outcomes.