Untitled Blog Engine

Recovery
The old posts certainly weren’t earth-shattering, but they do contain some nice old screenshots from the early days of iOS and Web 2.0. Old podcast workflows, fun with Telekom, and things like that.
Recovering the old blog posts from the Wayback Machine took a few hours, mainly because I had to track down the last few missing images. Those were actually still in the incomplete Dropbox backup, which had looked intact all along but turned out to be mostly empty and had caused this whole mess in the first place.
Converted the HTML to Markdown with YAML front matter containing metadata from the WordPress database. Date, post ID, slug.
Gathered all the images, removed embeds, and fixed a few links to old podcasts.
Untitled Blog Engine
My preferred stack for small and larger websites like this is Golang + SQLite. Two files and the website is up and running.
In six hours, I built this site with Codex in just over 3,000 lines of code.
Features:
- One Go executable: server, templates, CSS, admin JavaScript are all embedded. Pure-Go SQLite keeps builds free of a C toolchain dependency.
- Server-rendered HTML: standard Go templates, small local stylesheets, no public JavaScript or external font/framework downloads. Very little for the browser to load.
- SQLite for persistent state: content, settings, passkeys, and sessions in one database. WAL mode, one serialized writer, and four read-only connections allow concurrent reads and edits.
- Render when content changes: HTML, excerpts, and social descriptions are stored transactionally. Public requests read prepared content; renderer versions trigger backfills when rendering rules change.
- An archive that survives migration: roughly 250 recovered posts, original URLs, German/English versions, and stable RSS identifiers that preserve old WordPress IDs.
- A usable publishing workflow: posts and pages, drafts, private previews, scheduled publication with timezone support, admin search/pagination, and browser-local draft recovery.
- Passkey authentication: WebAuthn login, one-time initial setup, and signed cookies backed by revocable database sessions.
- S3-compatible image storage: uploads insert Markdown into the active language editor; UUID filenames support long-lived immutable caching.
- Straightforward operations: Git-push deployment to server, Caddy for HTTPS/compression, systemd, health checks, Monit, and daily offsite SQLite backups.
- Caching at the edge: Cloudflare caches public HTML/RSS for five minutes while admin responses stay uncached. Publishing changes can therefore take five minutes to appear.
- Performance guided by measurements: narrow queries and covering indexes keep Markdown out of listing reads. Recorded Linux handler benchmarks put the homepage around 0.9 ms, excluding networking, TLS, and browser work.
- The useful lesson: moving rendering work to saves and keeping the public page lightweight got us further than adding infrastructure.
The entire engine is designed around bilingual posts, and most posts got an automatic translation.
Deployment to the server happens via git push prod, which builds the Linux binary directly on the server and creates a backup beforehand in case a rollback is needed. Caddy sits in front of the blog and also serves plenty of other services for other purposes.
Media files are uploaded directly to R2, Cloudflare’s S3-compatible object storage. Cloudflare also handles caching in front of the entire site. The caching headers should ensure that almost no traffic actually reaches the blog server, and performance should be virtually indistinguishable from a statically generated site.
The design could certainly use some more work, but it’s better than nothing. The backend isn’t for the faint of heart, but I can manage.