Building krishaynes.co.uk
The article you are reading is part of the thing it describes. That feels worth admitting up front, because it is the whole point.
This site is not a blog in the sense that word usually means. It is a public engineering notebook, a place where every project I actually build gets written up properly, in plain text, under version control, on infrastructure I own. The page rendering these words was a Markdown file before it was a web page. It lives in a Git repository next to every other thing I have written. If this domain disappeared tomorrow, the content would not, because the content is not really “on” a website at all. It is a folder of text files that happens to be published as a website.
I did not start here. For years I had a Blogger site, like a lot of people who wanted somewhere to put their thoughts and did not want to think about hosting. Moving off it was one of those decisions that felt like procrastination at the time and turned out to be one of the better engineering choices I have made, precisely because it stopped being about a website and started being about how I keep knowledge. This is the honest account of why I moved, what I built, and the trade-offs I accepted.
What was actually wrong with Blogger
Blogger worked. That is the uncomfortable part. It did exactly what it promised. You log in, you click into a WYSIWYG editor, you type, you hit publish, and a post appears on the internet. For a long time I told myself that was enough.
The problem is not that these platforms are bad at publishing. The problem is what they do to the content itself, and it took me a while to see it clearly.
Start with ownership, because everyone says they own their content and almost nobody does. On Blogger my words lived in Google’s database in a format I never saw and could not meaningfully export. There was an export button, of course, that produced an XML file full of platform-specific markup and embedded styling decisions I never made. That is not ownership. Ownership is being able to pick up your work and walk to a completely different tool without a migration project. If leaving costs you days of cleanup, you do not own your content, you are renting space in someone else’s schema.
Then there is fragility. A WYSIWYG editor hides the actual content behind a wall of generated HTML. Paste something in from Word and you inherit a tangle of <span style="..."> tags that follow you forever. The text you see is not the text that is stored. You are editing a rendering of your content, not the content, and the gap between those two things is where rot sets in. I had posts where a theme change broke the formatting in ways I could not fix without going into raw HTML the platform did not really want me touching.
And there is no real history. This is the one that finally got me. If I edited a post and made it worse, there was no clean way to see what it used to say, no diff, no “what changed and when”. The current state was the only state. For a personal blog that sounds like a non-issue. For an engineering journal, where the value is in being able to look back and see how my thinking evolved, it is fatal. I want to be able to ask “what did I believe about local models a year ago” and get an answer from the record, not from memory.
The deepest problem, though, is existential. A SaaS blogging platform is a building you do not own, and one day the landlord decides to demolish it. Google has a long, well-documented history of closing products people depended on. Blogger has survived, but I no longer wanted to bet a decade of writing on the continued enthusiasm of a team inside a company that does not particularly care about it. When the platform dies, the content dies with it, unless the content was never really trapped inside the platform in the first place.
If leaving a tool is a project, you never owned your work. You were renting.
So the brief I set myself was simple. I wanted a way of writing that would outlive whatever I used to write it.
The choices, and the ones I rejected
Three decisions sit underneath this site, and they are deliberately boring, which is how you know they will last.
Markdown, because plain text outlives tools. I write everything in Markdown now, and the reason is not that Markdown is elegant, though it is. It is that a Markdown file is a plain text file. I can open it in anything, on any operating system, in forty years, with no special software. It is human-readable as source and renders cleanly as output, so there is no gap between what I store and what I mean. It diffs beautifully, line by line, which makes change history actually useful. It is the lingua franca of technical writing, so it moves between tools without translation.
There is a fourth reason that matters more every month: Markdown is the format machines read best. The same notes I write here feed Atlas, my local AI assistant, whose knowledge base is a folder of Markdown under Git. Plain text is what retrieval systems want, what models were trained on, what tooling parses without heroics. By writing in Markdown I am not choosing between writing for humans and writing for machines. I am writing once and getting both. That single decision is why this site and my second brain are, underneath, the same thing, which is the argument I make in building knowledge instead of documents.
Git, because content deserves the same rigour as code. Once the content is plain text, putting it in Git is obvious, and it changes everything. The content is a repository. Every change is a commit with a message and a date. I can branch, draft a controversial post on a feature branch, sit on it for a week, and merge it when I am happy, or throw the branch away with no trace. I can recover anything. A post I deleted two years ago is one git log away. The history is not a feature bolted onto the content, it is inseparable from it. This is exactly the discipline I keep my homelab in, where compose files in Git are the source of truth and the running containers are just the current checkout, as I describe in lessons from building a Docker homelab.
Hugo, because I wanted a website, not a database. With plain text in Git, I needed something to turn it into a site. The honest alternative was WordPress, and I rejected it deliberately. WordPress means a database, a PHP runtime, a plugin attack surface, and a backup story that is genuinely hard to get right. It reintroduces every fragility I was trying to escape. A static site generator does the opposite. Hugo takes my Markdown and a theme and produces plain HTML files. There is no database. There is nothing to hack at runtime because there is no runtime, just files a web server hands out. It is absurdly fast, builds the entire site in well under a second, and it has a mature theme ecosystem. I chose hugo-theme-stack because it is clean, card-based, handles categories and tags well, and renders Mermaid diagrams and code blocks without me fighting it. The diagram below this paragraph is just a fenced code block in the source; the theme turns it into a picture.
I considered Jekyll and Eleventy. Jekyll’s Ruby toolchain has caused me more grief than Hugo’s single binary ever has. Eleventy is lovely but I did not want to assemble a site from JavaScript plugins. Hugo is one binary, no dependencies, no node_modules, and it is fast enough that the build never breaks my train of thought.
Docker, because the build should be reproducible. I did not want to install Hugo on my laptop and discover in a year that a version bump had broken my site. So Hugo runs in a container, defined by the compose file that lives in this very repository. The toolchain is pinned and reproducible. Anyone, including future me on a fresh machine, can clone the repo, run one command, and get an identical local preview. This is the actual service:
services:
hugo:
image: ghcr.io/gohugoio/hugo:latest
container_name: hugo-site
working_dir: /src
volumes:
- .:/src
ports:
- "1313:1313"
command:
- server
- --bind
- 0.0.0.0
- --baseURL
- http://192.168.100.250:1313
- --buildDrafts
- --disableFastRender
restart: unless-stopped
docker compose up and I have a live-reloading preview on the lab network. Save a Markdown file, the browser refreshes, the loop is instant. For a production build I swap the server command for a plain hugo invocation and the container spits out a public/ directory of static files, which is all a web server ever needs to see.
How it actually fits together
The architecture is deliberately almost nothing, and that is the feature. There are no moving parts at serve time. All the cleverness happens at build time and then gets frozen into static files.
The same Markdown that flows down the left into a published page also flows right into the knowledge base my assistant reads. One source, two consumers. The website is the human-facing output; the retrieval index is the machine-facing one. Neither is the canonical store. The Git repository is.
A single article is just a file with a block of YAML front matter at the top, which is how Hugo knows the title, date, and where to file it under categories and tags. Here is the shape every post takes:
---
title: "Building krishaynes.co.uk"
date: 2026-06-25
description: "Why I moved off Blogger and built this site on Hugo and Git."
draft: false
categories:
- Projects
- Infrastructure
tags:
- hugo
- markdown
- git
- static-site
---
The information architecture matters more than it looks. The content is organised into sections, which are just top-level folders: projects, ai, infrastructure, automation, citrix, notes, and posts. Sections are the spine, the structural home of a piece. A write-up of something I built goes in projects. A piece of thinking about where the industry is going goes in posts. A half-formed working note goes in notes, which is where you will find what I’m building now, the rolling log of current work.
Categories and tags do a different job, and conflating them is a classic mistake. A piece lives in exactly one place on disk, but it can touch several themes. Categories are the broad buckets, capitalised, a handful of them: AI, Infrastructure, Automation, Projects, Knowledge. Tags are fine-grained and lowercase: hugo, ollama, docker, n8n. The section says where the article lives. The categories say what kind of thing it is. The tags say what it touches. Get that separation right and the site stays navigable as it grows; get it wrong and you end up with forty near-identical tags and no categories that mean anything.
How I actually intend to use it
A blog is something you maintain. A notebook is something that compounds. The distinction is the entire reason this exists.
The discipline I am holding myself to is straightforward: every project gets written up here. Not a polished marketing case study, but the real account, including the parts where I got it wrong. The lab I run, described in building an AI infrastructure lab at home, is the raw material; this site is where that work gets turned into something durable. When I size a GPU, configure Caddy, debug an n8n workflow, or work out a battery charging strategy, the writing-up is part of finishing the job, not an optional extra after it.
That is the second-brain argument I make at length in building a second brain. Knowledge you do not write down decays. Knowledge you write down into a trapped platform decays slightly slower and then dies with the platform. Knowledge you write down into versioned plain text, that both you and your AI can read, accumulates. Each article makes the next one easier to write, because I can link to settled thinking instead of re-deriving it. The cross-references in this very paragraph are the mechanism: the site is becoming a graph, not a pile.
It also forces clarity. You cannot write up a thing you only half understand. The act of explaining the build, in prose, to a hypothetical reader, repeatedly exposes the bit I was hand-waving over. Writing is how I find the holes.
What I got wrong, and the honest trade-offs
I will not pretend this is free. The move off Blogger bought me durability and ownership and cost me convenience, and anyone considering the same should know the bill.
There are no comments out of the box. A static site has no server to receive them. You can bolt on a third-party comment system, but that is exactly the kind of external dependency I left Blogger to escape, so I have simply gone without. The conversation happens elsewhere, and I have made my peace with that.
You write Markdown, not click buttons. For me that is a feature, but it is a real shift. There is no toolbar. If you want a table you write a table in pipes and dashes. For anyone not already comfortable in a text editor, that is friction, and pretending otherwise would be dishonest.
There is a static-site learning curve, and I underestimated it at first. Hugo’s templating, the way sections and taxonomies and front matter interact, the occasional cryptic build error, the theme’s configuration options buried in a TOML file. None of it is hard once it clicks, but the first weekend was not the frictionless experience the “just write Markdown” pitch implies. My first attempt at the section structure was wrong. I dumped everything into posts and used tags to fake structure, and the site became an undifferentiated stream within about fifteen articles. Reorganising into real sections later meant moving files and fixing internal links, which Git made survivable but which I should have got right from the start.
I also over-tuned the theme early, fiddling with colours and card layouts before I had written anything worth reading. Classic procrastination dressed as work. The lesson, which I keep relearning, is that the content is the product and the presentation is a distant second.
Where this goes next
The roadmap is modest and concrete, which is how I like roadmaps.
The most obvious next step is closing the loop with Atlas. Right now the same Markdown feeds both the site and the assistant’s knowledge base, but the wiring is manual. I want the publish step and the index step to be one action, so that anything I write is immediately retrievable by the assistant without me copying files around. That is a small n8n workflow away.
I want to automate the build and deploy properly, so that a git push triggers a container build and pushes the static output to where it is served, with no manual steps. The pieces are all in the lab already; it is a matter of joining them.
Longer term I would like a lightweight way to surface related articles automatically, generated from the actual content rather than hand-maintained tags, which is the kind of thing a local model is genuinely good at. And I want to be more disciplined about the rolling notes section, treating it as a daily-ish log rather than something I tidy up only when a project is finished.
None of this is exotic. It is all the same pattern: plain text, in Git, processed by reproducible tooling, serving both me and the machine I am building.
The meta point
I will end where I started, because the recursion is the argument. This article is a Markdown file in a Git repository, built by Hugo in a Docker container defined by a compose file three folders away, and it exists to explain why it is a Markdown file in a Git repository built by Hugo in a Docker container. The medium is the message, and the message is that the medium should outlive the platform.
Blogger gave me somewhere to publish. This gives me somewhere to keep. The difference between publishing and keeping is the difference between a stream that flows past and a body of work that accumulates, and once I saw that difference I could not unsee it. A platform that can disappear is not a place to put a decade of thinking. A folder of plain text under version control is the closest thing I have found to permanence in software, and permanence is exactly what an engineering journal needs to be worth keeping.
Every project from here gets written up in this format. The notebook compounds. That was the whole point.