Why I went back to a static site

Every dynamic blog I have run eventually broke in a way that had nothing to do with writing. Here is the case for plain files.

A row of plain file icons on a dark blue background
On this page

I have rebuilt this site more times than I have written posts for it, which is its own kind of confession. WordPress, then a Rails app, then a Next.js thing with a headless CMS. Each rebuild was justified at the time. None of them made me write more.

This post is plain .md rather than .mdx — no components, no imports. It is here partly to make a point: most posts never need more than markdown gives you, and the ceremony of the fancier format is optional.

The failure mode is always the same

A dynamic blog has a database, a runtime, and a deploy target that all need to be alive at the same moment somebody clicks a link. That is three things that can rot while you are not looking. Mine rotted in this order, reliably:

  1. A dependency picked up a breaking change in a minor release.
  2. The hosting provider changed its free tier.
  3. The CMS I was using got acquired.

None of that is about writing. All of it landed on a Saturday.

The database was the worst of it

The Rails version had eleven tables. Nine of them existed to model things a blog does not actually have: drafts with workflow states, a taxonomy with hierarchy I never used, a comment system with moderation queues that only ever held spam.

The database was not storing my writing. It was storing the accumulated weight of features I had built because they were easy to build.

The markdown files that replaced all of it are, collectively, smaller than the schema migration folder was.

Plain files don’t rot

A static build has one output: a directory of HTML, CSS and images. It can be served by anything — a CDN, an S3 bucket, a spare machine, or this, if it comes to that:

Terminal window
cd dist && python3 -m http.server 8000

There is no runtime to patch and no database to migrate. If the build tool disappears tomorrow, the dist/ folder still works, and the markdown is still markdown.

The performance argument is the second-best one

It is real, though. There is no server round trip, no hydration, nothing to do but paint:

What happens on a clickDynamic blogThis site
Database queryyesno
Template renderper requestat build
JS bundle to hydrate100–200 KB~0 KB
Time to first paint300–600 ms30–60 ms

But speed is not why I moved. The best reason is that the thing survives my own neglect. A static site has no uptime to speak of because there is nothing running to fall over.1

What you give up

Being honest about the trade:

  • Comments. Gone, unless you bolt on a third-party service — which puts you right back to depending on someone else’s runtime.
  • Search, unless you precompute an index. That is what I do here; it ships as static files and runs in the browser.
  • Anything genuinely personalised. A blog does not need it.
  • Instant publishing. A post goes live when the build finishes, not the moment you hit save. In practice that is about ninety seconds.

That is a short list, and I would rather pay it than spend another Saturday upgrading a CMS so that a post from 2019 keeps rendering.

Would I recommend it

If you are writing for yourself and you are comfortable with a text editor and git: yes, without hesitation.

If you need non-technical people to publish without help, no — that is exactly the problem a CMS solves, and solving it yourself with a static site generator takes longer than you think. Pick the boring hosted thing and get on with it.

The point was never that static is objectively better. It is that I kept choosing tools that needed maintenance, and then resenting the maintenance.

Footnotes

  1. Your host can still fall over, obviously. The difference is that recovering means copying a folder somewhere else, not restoring a database and hoping the schema still matches the code. ↩