Skip to content

Deployment

CaretCMS runs anywhere Astro does. The constraints are:

  1. output: 'server' (or hybrid with editor pages non-prerendered)
  2. The storage adapter has to match the host’s filesystem semantics
  3. Editor secrets need to be set as environment variables

This page walks through the four common shapes.

Run through this before any production deploy:

  • output: 'server' in astro.config.mjs
  • CARET_EDIT_PASSWORD set in host env (or editing disabled)
  • CARET_SESSION_SECRET set to a long random value, not committed to git
  • HTTPS enforced on your domain
  • Storage adapter matches the host (see table below)
  • If you don’t want editing on this environment, set enableAdmin: false and enableInlineEditor: false
HostAdapterNotes
Node (Fly, Railway, Render, your VPS)Filesystem (default)Mount a persistent volume; otherwise edits vanish on redeploy
Cloudflare WorkerscloudflareStorage + r2UploadsKV + R2 bindings required
VercelCustom (Postgres / KV)No writable filesystem
NetlifyCustom (Postgres / KV)Same — serverless, ephemeral disk
Static-site hostNot supportedCaretCMS needs a server

The simplest path. Filesystem storage works as long as you have one process and a persistent disk.

  1. Install the Node adapter

    Terminal window
    npm install @astrojs/node
  2. Configure Astro

    astro.config.mjs
    import { defineConfig } from 'astro/config';
    import node from '@astrojs/node';
    import caret from '@caretcms/core';
    export default defineConfig({
    output: 'server',
    adapter: node({ mode: 'standalone' }),
    integrations: [caret()],
    });
  3. Mount a persistent volume

    CMS edits land in .caret/data/. If your host wipes the filesystem on each deploy, mount a volume there.

    fly.toml
    [mounts]
    source = "cms_data"
    destination = "/app/.caret/data"
  4. Set environment variables

    Terminal window
    CARET_EDIT_PASSWORD=...
    CARET_SESSION_SECRET=...

    Set these in your host’s dashboard or CLI. Never commit them.

Once deployed:

  • Visit https://your-site/admin over HTTPS — confirm cookie has Secure flag (devtools → Application)
  • Try logging in with the wrong password — should reject
  • Try POST /api/cms/mutate without the CSRF header — should return 403
  • Try POST /api/cms/mutate without a session cookie — should return 401
  • Confirm /admin is gated (no editor JS on public pages without the cookie)
  • If Cloudflare: check wrangler tail for errors after a save
  • If Node: check the host’s logs for [caretcms] messages on boot

If your prod environment shouldn’t be editable (you only edit on staging, then promote):

caret({
enableAdmin: import.meta.env.MODE === 'staging',
enableInlineEditor: import.meta.env.MODE === 'staging',
})

In prod, no admin routes are mounted, no inline editor JS is injected. Pages still render stored content (the rewriting middleware still runs); they just can’t be edited from the browser.