When a client asks for an editorial site — a company blog, a product documentation hub, a thought-leadership platform — the choice of stack shapes everything that follows: how fast the site loads, how painlessly editors push content, and whether the engineering cost scales sensibly as the site grows.

After evaluating a dozen combinations, we landed on Astro 5 + Keystatic 0.5 deployed on Cloudflare Workers. Here is what drove that decision, and what production use has taught us.

The baseline requirements

Our clients’ editorial sites share a consistent set of requirements:

Most combinations hit two or three of these. Astro + Keystatic hits all four.

Why Astro

Astro’s rendering model starts from a principle that others treat as an optimization: every page is static unless you explicitly opt into server rendering. The result is that editorial content ships as pre-built HTML — no JavaScript hydration penalty, no SSR cold start, no database round-trip per request.

For editorial sites where 90% of traffic hits the same 20 articles, this matters. We measured a consistent 97–99 Lighthouse Performance score on pages generated by Astro versus 82–88 for the same content in a Next.js App Router build that leaned on ISR.

Astro also handles multilingual content naturally through its Content Collections API. Our standard editorial template ships with zh-TW and en content parity as a first-class schema constraint — not an afterthought added six months later when the client discovers they need a second language.

Why Keystatic

The CMS market splits into two unhelpful extremes: WordPress-class tools with enormous surface area that most editorial teams use at 5% capacity, and headless CMS subscriptions that charge per seat and per API call and require a developer to update the content schema.

Keystatic occupies the gap. Content lives in your own Git repository as plain Markdown files. Editors get a clean UI at /keystatic. Changes are Git commits — the same commits that trigger your CI pipeline, create audit trails, and survive the closure of any vendor.

The pricing model is a significant talking point with clients: Keystatic is free, open source, and the content storage cost is whatever GitHub costs (zero for public repos, a few dollars a month for private). There is no per-seat fee that scales uncomfortably as the editorial team grows.

The Cloudflare Workers advantage

Static sites can live on any CDN. We chose Cloudflare Workers specifically because it gives us a path from “static blog” to “edge-rendered application” without a stack change. When a client eventually needs user authentication, personalised content, or server-side A/B testing, we extend the Worker — we do not migrate the entire site.

The Keystatic admin UI requires a live server to handle GitHub OAuth callbacks. Running this on Cloudflare Workers means the CMS backend costs the same as the static site: essentially zero at editorial traffic volumes, with no separate backend deployment to maintain.

What we learned in production

Three things surprised us in practice.

The deployment setup is non-trivial the first time. Deploying Astro to Cloudflare Workers Static Assets involves specific configuration that differs from Cloudflare Pages. We documented 14 specific failure modes during our first deploy — everything from npm ci rejecting lock files generated with --legacy-peer-deps, to the Cloudflare Workers not_found_handling setting silently swallowing dynamic admin routes. The good news: every trap is solvable, and after the first project we’ve pre-baked every fix into our scaffolding tool so subsequent projects inherit the solutions automatically.

Keystatic 0.5 requires a GitHub App, not an OAuth App. This distinction is not immediately obvious from the documentation. It matters because GitHub Apps support fine-grained repository permissions and installation tokens, which is what Keystatic uses internally. OAuth Apps don’t interoperate. The only reliable diagnostic is to grep the compiled Worker bundle for the env var names it actually reads — the list is authoritative.

Static generation means deployment equals publishing. On a database-backed CMS, you can draft content in the database and choose when to surface it. With Astro’s static build, merging to main triggers a build and deploy. We handle this by filtering status !== "published" in getStaticPaths — drafts exist in the repo but are never rendered to HTML until the status flag is changed. Simple and reliable.

Is this the right stack for your project?

Astro + Keystatic is a strong fit for:

It is a worse fit for:

The common thread: editorial sites are a solved problem, and the solution should be boring. Fast static HTML, a Git-native CMS, edge delivery, and zero operational overhead. That is the stack.