Why I rewrote this blog 5 times

The reasons, frustrations, and tradeoffs behind migrating from a Hugo prototype to Jekyll, Obsidian Digital Garden, Quartz, and finally Astro.

My blog has been rewritten 5 times. Not because I enjoy migrating Markdown files for fun. Each rewrite started the same way: I hit a wall with the current tool, convinced myself the next one would be the last, and found a different wall a year or two later.

2019: A Hugo prototype on ricardof.dev

The first version went live in 2019 on ricardof.dev. Unfortunately, I do not have the source code anymore, and I do not remember which tool I used to build it. (it was probably Hugo!).

The blog was entirely in Portuguese and it had a few articles about Flutter and customizing the Tilix terminal, plus a page listing my Keypirinha and Albert launcher plugins. I basically abandoned the blog for a long time and when I came back in 2022, I didn’t remember anything, so I started over.

Blog homepage in 2019: Hugo prototype on ricardof.dev

2022: Jekyll (the obvious starting point)

Jekyll made sense in 2022 because it was the default GitHub Pages path and it was the right choice. Push Markdown to the default branch, let GitHub build it, point blog.ricardof.dev at the result.

The setup was simple: _config.yml had lang: pt-BR, permalink: /:slug, markdown: kramdown with GFM input, Rouge syntax highlighting, and pagination at 20 posts per page. I used a minimalist theme and kept the content in the same Git repository as my Obsidian vault. .obsidian went into exclude so Jekyll would ignore the vault metadata.

The blog was mostly written in Portuguese, but I also had some posts in English, like the Fedora spec files note and a minidlna Docker setup. Just posts in two languages mixed into the same feed. It bothered me, but not enough to fix it then.

Liquid was what pushed me away from it. Every layout change sent me into _includes/ and _layouts/, and I kept forgetting Jekyll’s directory rules between edits. I wanted the blog to live inside Obsidian, not beside it as a project that happened to share a folder.

Blog homepage in 2022: Jekyll with a minimalist theme

2024: Obsidian Digital Garden (posts inside the vault)

The Obsidian Digital Garden plugin got the workflow almost right: install a plugin in Obsidian, mark notes with publish: true, run a command, and publish to GitHub Pages.

Every post, draft and private note lived in my Obsidian vault. That was the appeal for me and also the part that made me nervous. I could link between public posts and private notes without thinking about paths, but one wrong toggle could publish something personal. I never leaked anything, but I checked the publish flag on every note before running the command.

Sometimes the publish command ran and nothing appeared. Other times it created one commit per article and made the Git history look ridiculous.

Also, customization was thin too: one CSS file, a few plugin options, and not much room beyond that.

Still, this migration changed the direction of the blog for good. I translated the remaining Portuguese posts to English and decided future posts would be English-only.

Blog homepage in 2024: Obsidian Digital Garden with graph view

Quartz is built for Obsidian vaults: callouts, wikilinks, backlinks, graph view. It was the first time where I could open my blog and see my Obsidian habits survive the build. Callouts looked right, wikilinks landed on the right posts, backlinks showed up where I expected them. Quartz is a template, not a framework. You fork jackyzha0/quartz, add your content, and then keep merging upstream changes so the fork does not drift too far.

Changing the font meant reading the Quartz source to understand the theme system. A print stylesheet felt like swimming against the project. Static search was another mismatch, because Quartz used client-side search. I kept making small changes and feeling like I had to ask the codebase for permission.

Build speed was not really a issue: a build took about 5 seconds for 23 posts and produced 162 files. The really annoying part was keeping my customizations while still being close enough to upstream to pull updates.

The Quartz v5 migration guide existed, but the upgrade still broke things. Callouts stopped rendering, the layout shifted and so on. By then I had been watching Astro for a while, so I tried it over a weekend and did not go back.

Blog homepage in early 2026: Quartz with backlinks and graph view

Mid 2026: Astro (I own every file)

Astro is a general-purpose static site generator. What convinced me was the content collections API and Astro’s island model: components render to static HTML by default, and JavaScript only shows up where I ask for it.

The migration from Quartz took about two days. The hard part was making the site feel editorial and deliberate without turning it into another template. I wanted something less raw than the old Jekyll layout and less boxed in than Quartz.

The current build runs in about 4 seconds for 89 pages. Pagefind indexes the content in about 0.1 seconds and it gives me a static search index with no server.

I added a print stylesheet because I actually print long posts to proofread them. It removes navigation, sidebar, and comments, leaving the article body. For the first time I have comments in my articles! I’m using Giscus at the bottom of each article, backed by GitHub Discussions in a separate blog-comments repository (no analytics and no cookies).

Custom remark and rehype plugins transform [[wikilinks]] and > [!callout] blocks at build time and the raw Markdown files still look like Obsidian notes.

Migrating from Quartz meant rewriting the frontmatter of every post to match Astro’s content collection schema. I built the wikilink and callout plugins from scratch, created components for article layout and tag pages, and set up the taxonomy pages: /articles, /guides, /notes, /posts, and /tags. The first npm run build caught most of the mistakes.

I still use Obsidian to write my articles, I still use wikilinks and callouts. Then, I commit Markdown files to Git and Astro handles the site. The workflow stayed the same. The full stack is documented at /colophon.

What I would do differently

I should have forced a frontmatter shape earlier for all my articles. The migrations were full of dumb YAML cleanup: layout: post in Jekyll, plugin fields in Digital Garden, Quartz tag handling, then Astro’s stricter content collections. The boring fix would have been choosing title, description, date, tags, and type in 2022, then writing a tiny converter each time I moved tools. I did the opposite and paid for it in hand-edited YAML. Astro now catches missing fields during npm run build, which is exactly the kind of boring failure I want.

I do not think the earlier choices were wrong. Jekyll was cheap and obvious, Obsidian Digital Garden got me writing inside the vault and Quartz proved the Obsidian Markdown idea was worth keeping. Astro is the first version where I do not hesitate before changing layout code.

Comments

Back to top