3 min read
Websites Are Files
Hugo for every site that doesn't need a backend
The tenth and last part of the boring stack. The first nine were about apps. This one is about the simpler case: websites, like a blog, a campaign site or an information page for an association.
They don’t need a backend. So they don’t get one.
Generated once, served as files
Hugo is a static site generator. It takes Markdown files, templates and a config file and produces a folder of plain HTML, CSS and images. That folder is the website. Any web server can serve it, and on my server, Caddy does.
What’s missing is the point:
- No runtime. Nothing executes when someone visits. There’s no PHP, no Node process, no application server.
- No database. The content is in Markdown files in Git, with the full history.
- Nothing to patch. A WordPress site needs updates for its core, its theme and every plugin, forever. A folder of HTML files has no security updates.
- Nothing to break under load. Serving files is the one thing every web server does extremely well.
Why Hugo in particular
- It’s one binary, written in Go. Install it, and that’s the whole toolchain. No
node_modules. - It’s fast. A site with a few dozen pages builds in well under a second.
- It does multilingual sites properly. Translations are file suffixes:
post.mdandpost.de.md. - It processes CSS and images itself, so I don’t need a separate build pipeline.
Write your own theme
This site is the cautionary tale. For a year it has been a polished portfolio template in a different framework, and I spent more time adapting the template than writing. I’m moving it back to Hugo, with a handful of templates and one stylesheet written for this site only.
A theme brings someone else’s decisions and someone else’s update cycle. For a site that is mostly text, the templates are small enough to own.
For sites I migrate from WordPress, a well-maintained theme like PaperMod with a few overrides is fine. The rule is the same either way: change the theme only through your own overrides, never edit it directly, so it can still be updated.
Deploy is a copy
Deploying a static site means copying the changed files to the server. rsync compares contents and only uploads what changed. It also removes files that no longer exist, which is why each site has its own folder on the server, and that folder has to exist before the first deploy.
What it costs
- No dynamic features without help. Comments, search, forms and newsletters need an external service or a small backend. Most sites need fewer of these than they think.
- Editing means Git and Markdown. For me that’s an advantage. For a non-technical editor, it’s a hurdle, and a hosted CMS might fit better.
- Templates use Go’s template language, which takes some getting used to.
The whole stack
That’s the boring stack: Go, the standard router, HTML from the server, SQLite, hand-written SQL, migrations in the binary, tests without mocks, Docker for development, one server and static files for websites.
None of these choices is exciting. Each one removes something I would otherwise have to run, update or debug. Together, they let one person build and run several apps in their spare time. That’s what I want from a stack.