Why I Shipped a Git-Based Blog Instead of Paying for a CMS
The short answer: my blog is a folder of markdown files and one build script. No CMS, no subscription, no plugin updates. A post goes live with a commit, and every page it generates carries the same measurement layer as the rest of my portfolio.
Here is the longer answer, because the decision is more interesting than the stack.
What a CMS actually costs a marketer
The subscription is the smallest part. The real costs are the ones that show up later.
- Tracking control. Hosted platforms inject their own scripts and limit what you can put in the page head. My portfolio runs a documented 15-event dataLayer schema with an on-page tracking console. A rented blog would be the one part of the site I could not instrument properly.
- Design drift. A themed blog never quite matches the site it hangs off. Same fonts if you are lucky, different spacing, different dark mode, different everything else.
- Lock-in. Export tools exist, and they are all bad. Markdown in a git repo is the most portable content format there is.
For a casual blog, none of this matters. For a portfolio whose whole argument is "I build the systems behind the numbers," outsourcing the publishing system would undercut the pitch on every page.
What I built instead
The pipeline is deliberately small:
- Posts are markdown files with structured frontmatter: title, slug, date, description, keywords, tags.
- A zero-dependency Node script renders each file into the site's design system. Same stylesheet, same navigation, same light and dark themes as the case studies.
- The build emits what SEO actually requires: title tag, meta description, canonical URL, Open Graph tags, and Article JSON-LD, all generated from frontmatter, never hand-written.
- Each post page registers in the same dataLayer as the rest of the site, so scroll depth, section views, and reading behavior land in one measurement system instead of two.
- The script also syncs sitemap.xml and the llms.txt page index, so search engines and AI screeners learn about a new post in the same commit that publishes it.
Publishing is one command and one commit. Rollback is git. Staging is a branch.
The part that surprised me
The build script was not the hard part. The hard part was deciding what the generator should refuse to do.
It refuses to publish a post with incomplete frontmatter. It refuses silently invented structure: the H1 comes from the title field, and meta tags come from the description field, so a lazy draft fails loudly instead of shipping half-optimized. Constraints in the pipeline mean quality does not depend on me remembering a checklist at 11pm.
That is the same principle I apply to marketing operations work: encode the standard into the system, not into willpower. It is how a UTM governance project takes attribution coverage from 71% to 100%, and it is how this blog stays consistent without an editor.
Who should not do this
Honest scoping, because this approach is not for everyone:
- If you do not live in a code editor, a CMS is the right call. The friction would eat your publishing habit.
- If a team of non-technical writers needs to publish, rent the CMS.
- If you need comments, memberships, or paywalls, this is the wrong architecture.
I am one operator publishing proof of work. The repo is the CMS, and that is a feature.
The takeaway
Pick the publishing system that strengthens your story. Mine is that I build measurable systems, so my blog had to be one: instrumented, versioned, and generated by a script I can show people. The blog you are reading is the case study.
If you are weighing the same decision for a portfolio or a small marketing site, the architecture above is reproducible in a weekend. And if you want someone who builds this kind of system for a marketing team, get in touch.