Flat-file CMS in 2026: when files beat a database
Markdown files with frontmatter still run a large share of the web's small sites. What the flat-file model gets right, the four places it breaks, and how to tell which side of the line your project is on.
A flat-file CMS stores content as files on disk. No Postgres, no MySQL, no content API: a folder of markdown, some YAML frontmatter, maybe a JSON index. It is the oldest idea in web publishing and it keeps coming back, because for a large class of sites it is simply correct.
It is also routinely recommended for projects it will actively harm. Here is how to tell the difference.
What the model actually buys you
Nothing to operate. There is no database process to patch, no connection pool to size, no backup job to verify, no restore to test at three in the morning. Your content is files. cp -r is a complete backup strategy, and it works.
Version control comes free. Put the folder in Git and you have per-change history, diffs a human can read, branches, and the ability to see exactly what changed between two versions of a page. No CMS feature list will ever match git diff for auditability.
Content survives the tool. This is the underrated one. A markdown file with frontmatter is readable in fifty years, by any editor, with no software required. Every hosted CMS eventually raises its prices, changes direction, or shuts down, and the question “how do we get our content out” always arrives. With flat files there is nothing to get out: you already have it.
Builds are fast and deterministic. Reading a folder of files is the cheapest possible content source. No network, no rate limit, no API that is slow this afternoon.
It grep-s. Find every page mentioning a product name across two hundred files, in one command. Try that on a headless CMS.
The four places it breaks
1. The person editing is not you
This is the big one, and it is not a technical objection.
Flat-file content is edited either in a code editor, which requires knowing markdown and having the repository checked out, or through a web interface layered on top, which brings us straight back to the git-based CMS question and its GitHub account.
A shop owner updating their opening hours from a phone on a Sunday evening cannot do either. That is not a failure of their intelligence, it is a mismatch: the model assumes the editor is comfortable with files, and most editors are not.
2. Media does not want to be a file
Text in Git is ideal. Images in Git are a slow-motion problem.
A client uploads a photo straight from their phone: four megabytes, four thousand pixels wide, in the wrong orientation. It goes into the repository. Next month they replace it, and both versions stay in history forever. A brochure site can accumulate several hundred megabytes of dead image data in a year, and every developer who clones the repo downloads all of it.
Then the real work starts: that photo needs resizing, converting to AVIF and WebP, serving from a CDN, and alt text that somebody has to write. A flat-file setup gives you none of that. You bolt on an image pipeline, and it is now a thing you maintain.
3. Relationships and queries
Flat files have no joins. “All services offered in a given city, sorted by price, that have a photo” is a filter over an in-memory array you assemble at build time. That works fine at two hundred entries. At five thousand it becomes a build-time cost and a piece of code you now own.
Anything genuinely relational, a product catalogue with variants, a booking system, a member directory with permissions, wants a database. Forcing it into files is a decision you regret in month four.
4. Concurrent editing
Two people editing the same file is a merge conflict, and a merge conflict shown to a non-technical editor is the end of that editor’s willingness to use the tool.
In practice this matters less than people claim, because two people rarely edit the same page of a small site at the same moment. But when it happens, the flat-file model has no good answer, and every answer it does have involves explaining Git.
Where the line falls
| Situation | Flat files | Something else |
|---|---|---|
| Personal blog, you write it | Yes, obviously | Overkill |
| Documentation, developers edit | Yes | Overkill |
| Brochure site, client edits | Only with a CMS layer on top | Likely |
| Twenty brochure sites, clients edit | The maintenance adds up | Yes |
| Product catalogue with variants | No | Database |
| Editorial site, several writers daily | No | Hosted CMS |
The pattern: flat files are excellent until a non-technical person has to write into them, and until images become a real volume. Those two things arrive together, and they arrive as soon as you have clients.
The hybrid that actually works
There is a third option that gets discussed less than it should, and it is the one we built on.
Keep the schema in files. Your content model, declared as TypeScript in your repository, versioned, reviewed in pull requests, diffable. That is code and belongs with the code.
Keep the content out of files. Put it behind an interface a non-technical person can use, with proper media handling, and hand it back as files whenever asked.
Then reconcile the two at build time: the CMS freezes the published state into an immutable snapshot, and your build reads that snapshot exactly as it would read a folder of markdown. From Astro’s point of view, nothing has changed. getCollection() works, typing works, the build is still a static build with no runtime dependency.
Concretely, with Menestrel, the model lives in cms.config.ts:
import { collection, defineConfig, fields } from '@menestrel/fields';
export default defineConfig({
project: 'atelier-morel',
locales: { default: 'fr', others: ['en'] },
collections: {
services: collection({
label: { fr: 'Prestations', en: 'Services' },
slugFrom: 'title',
fields: {
title: fields.text({ required: true, localized: true }),
summary: fields.textarea({ localized: true }),
price: fields.number({ unit: '€' }),
photo: fields.image(),
},
}),
},
});
That file is reviewed like code. The client never sees it. What they see is a form with four fields and a Publish button.
What you give up by leaving pure flat files
Being honest about the trade, because it is a real one.
You add a dependency you did not have. A hosted service is a company that can raise prices or disappear. Our answer is that the export is one command, on every plan including the free one, and produces markdown and JSON in content-collections format: the same shape you would have had with flat files. That is the only answer that means anything, and you should demand it of any hosted CMS you consider.
You pay. Three euros a month per site. Flat files are free.
Publishing takes a build. Reading local files is instant; a snapshot plus a rebuild is one to three minutes. If your content changes by the minute, this model is wrong for you and server rendering is right.
A note on the “just use Git LFS” answer
Whenever the image problem comes up, someone suggests Git LFS. It is worth addressing, because it solves less than it appears to.
LFS replaces binary files in your history with pointers, and stores the actual bytes on a separate server. Your repository stays small, which is the stated goal. In exchange you now have: a storage quota to watch on your hosting provider, a bandwidth quota that CI clones eat quickly, a dependency that every collaborator must install before they can check the project out, and a class of failure where a clone succeeds but the images are missing.
More importantly it does not touch the actual problem. The four-megapixel phone photo is still four megapixels. It still needs resizing, format conversion, and a CDN in front of it. LFS moves where the bytes live; it does not make them the right bytes.
If images are a meaningful part of your content, and on a brochure site they always are, they want a media pipeline rather than a storage trick. That means an upload that resizes and converts on the way in, derivatives served from a CDN, and alt text collected as part of the editing flow rather than as an afterthought nobody fills in.
What a flat-file setup costs you in year two
The first three months of a flat-file project feel excellent. The costs show up later, and they are worth naming so you can price them.
The config drifts from the content. Your collection has a price field. A client asks for a “from” price and a “to” price. You edit the schema by hand, migrate the existing files by hand, and hope you caught them all. There is no migration story, because there is no system that knows what the shape used to be.
The build gets slower in a way you cannot see. Reading files is fast. Reading files, then resizing every image, then generating derivatives, is not. On a site with two hundred photos this turns a four-second build into a ninety-second one, and nobody notices the day it happened.
Onboarding a colleague takes an afternoon. The knowledge of how this particular site’s content works lives in a config file and in your head. With twenty sites configured slightly differently over three years, that is twenty small bodies of tribal knowledge.
None of these is fatal. All of them are real, and none of them appear in the comparison table when you choose the tool.
How to decide
Write down who edits, how often, and how many photos they will upload in a year.
If the answer is “me, occasionally, none”, use flat files and stop reading blog posts about CMSes. If the answer is “my client, monthly, dozens”, the flat-file model will work for about six months and then start costing you in support time and repository weight.
If you are somewhere between, the comparison of git-based CMSes goes through the middle ground in detail, including the cases where the free tools remain the better call.