Home / Articles / Design Engineering
Design EngineeringStatic-first: the case for Astro over a CMS
Why we stopped reaching for WordPress and Ghost — and what a git-based, zero-database stack buys a small team.
TL;DR — For a small studio blog, a database-backed CMS is mostly liability you pay for daily: a server to patch, an admin login to defend, a backup to test. Astro flips the trade: content is Markdown in git, the output is static HTML, and the whole thing rebuilds in CI. You lose a slick admin UI; you gain a tiny attack surface, versioned content, and a site that’s effectively free to run. This very page is built that way.
For years the reflex was automatic: a new blog meant WordPress, or — if we were feeling modern — Ghost. Both are excellent at what they do. But “what they do” includes running a database, a PHP or Node runtime, a login page exposed to the open internet, and a steady stream of security updates that someone has to actually apply. For a small studio that ships client work all week, that someone is always busy. So we stopped reaching for a CMS by default, rebuilt our notebook on Astro, and haven’t missed it.
What a database-backed CMS actually costs
The headline feature of a CMS is the editor. The hidden cost is everything keeping that editor alive and safe. A WordPress install is a long-running server: a PHP process, a MySQL database, a public /wp-admin, and a plugin ecosystem that is the single most-attacked surface on the web. Ghost is leaner and far better engineered, but it’s still a Node service plus a database that you patch, back up, and monitor.
None of that is hard in isolation. It’s the steady-state burden that adds up:
- Patching. Every dependency is a clock. Miss a critical CMS or plugin update and you’re one drive-by scan away from a defaced site or a crypto-miner.
- A login to defend. A public admin panel invites brute-force and credential-stuffing traffic forever. You now own rate-limiting, MFA, and fail2ban-style hygiene.
- Backups you have to test. A database is mutable state, so “did the backup actually restore?” becomes a real question with a real answer you’d rather not discover during an incident.
- A bill. Even a small managed CMS runs $15–30/mo, plus your time, which is the expensive part.
A CMS asks you to defend a castle. A static site gives attackers a brick wall — there’s no login, no database, no runtime to compromise.
What static-first buys you
Astro is a static site generator: it renders your content to plain HTML, CSS, and a sliver of JS at build time, then ships files. No database, no application server in production, no admin login. The source of truth is Markdown in a git repository.
That single decision pays off in four directions:
- Attack surface near zero. There’s nothing to log into and nothing executing per-request. A static host serves files. The classes of vulnerability that dominate CMS incidents — SQL injection, plugin RCE, admin takeover — simply don’t have a target.
- Git as the source of truth. Every post is a reviewable diff. You get history, branches, rollback, and blame for free. Want to revert a bad edit?
git revert. Want a second pair of eyes? Open a PR. Want an AI to draft a post? It writes Markdown and opens a PR you review like any other change — no plugin, no API token, no write access to a live database. - Speed and cost. Static files behind a CDN are about as fast and cheap as the web gets. No cold starts, no query latency, no scaling story to write. Hosting is often literally free.
- Reuse the pipeline you already have. If you already run CI for client work, a blog build is just another job. Push to
main, CI builds, the output deploys. The same review gates and secret-scanning you trust for code now guard your content.
The editing experience is the honest trade-off. Markdown in an editor isn’t a polished WYSIWYG, and there’s no “publish” button for a non-technical author. For an engineering notebook written by the same people who write the code, that’s a feature, not a bug.
How it works in Astro
Astro’s content collections turn a folder of Markdown into typed, validated data. You define a schema once, and Astro fails the build if a post is missing a field or has the wrong type — your content gets the same guarantees as your code.
// src/content/config.ts
import { defineCollection, z } from "astro:content";
const blog = defineCollection({
type: "content",
schema: z.object({
title: z.string(),
pubDate: z.date(),
lang: z.enum(["en", "es"]),
featured: z.boolean().default(false),
}),
});
export const collections = { blog };
That’s the whole “backend.” Posts live in src/content/blog/, the schema enforces shape, and a typo in your frontmatter is a build error you catch in CI — not a broken page you discover in production.
When a CMS is still the right call
Static-first isn’t dogma. Reach for a real CMS when:
- Non-technical editors publish often and shouldn’t touch git or Markdown.
- You need memberships, paywalls, or gated content — anything that requires per-user server logic at request time.
- Content changes many times a day from multiple authors who need a queue, roles, and a scheduling UI.
- You’re running commerce or comments with live, mutable, user-generated state.
Those are real needs, and a database earns its keep there. But a studio’s engineering notebook is none of them. It’s a handful of authors, infrequent posts, and content that benefits from review.
The takeaway
If your blog is read far more than it’s written, and the writers can handle a Markdown file and a PR, default to static. Astro plus content collections gives you typed content, a build you already know how to run, and a site with almost nothing to attack or patch. You trade a fancy admin panel for fewer 2 a.m. pages — and for a small team, that’s the trade that keeps shipping.
The engineering & R&D notebook of Hagumi Studio. We write what we learn building and self-hosting the tools behind our work.
HAGUMISTUDIO.COM · X · RSS