Yet Another Site Revamp (devopsdirective․com v3.0)

At the time of publishing this article (2025-07-09), I hadn't published an article to this site in 602 days! 😱

I'm not 100% sure why this is, but I think it as at least partially due to the friction involved with creating and publishing posts.

Previous Setup

For a full history of the tooling for this site see:

  1. The Making of This Site (Hugo, Caddy, + GCP)
  2. CI/CD for this site (Hugo + Cloud Build)
  3. Static Site Hosting Using Google Cloud Storage and Cloudflare (with SSL!)
  4. Blog Workflow Improvements

Previously, the site was built using https://gohugo.io/. I authored posts in VS Code, either locally or using GitPod, and committed the changes to GitHub, where a GitHub Action handled building/pushing to Google Cloud Storage to be served.

old-site-setup.png
This worked fine, but didn't ✨spark joy✨

Despite my previous attempts to improve the workflow, the (small) friction of authoring in VS Code and interacting with Git directly were still annoying. I also had a couple of custom Hugo components for links and images which added additional micro-cuts to the workflow.

Should I use Ghost?

As I considered what to do, I once again revisited https://ghost.org/ as a potential option. It is slick, but requires either:

I played around with hosting it on Railway with this template, but ran into some wonkiness around:

  1. Healthchecks: I was unable to get Railway's healthchecks for the Ghost application working properly on Railway (which in turn prevents zero downtime deploys) for two reasons:
    • https redirect: Railway healthchecks are made without TLS and ghost returns a 301 redirect from http -> https. I think this could be worked around by setting this http header: X-Forwarded-Proto: https (but Railway does not allow setting custom headers for healthchecks).
    • hostname: Railway healthchecks originate from healthcheck.railway.app. Again, if we could set custom http headers, we could use: Host: devopsdirective.com, but we can’t.
  2. Volume Snapshots: Volume snapshots on Railway are only available to pro users. I could have implemented a separate mechanism to backup the database OR committed to this costing at least $20/month... but I'm irrationally frugal (sometimes to my own detriment)! 🤑
    railway-canvas.png
    That canvas view is 🔥 though!

Asking for Inputs

Two days ago I decided to see if anyone had recommendations for a blog setup they loved, and Victor Buendia recommended using Obsidian!

linkedin-help.png
He might be onto something 👀

I already use Obsidian for note taking, so the questions for me were:

  1. Can I make it do what I want it to?
  2. How seamless the publishing could be?

Obsidian Digital Garden

Turns out, a Norwegian legend named Ole Eskild Steensen has already done all of the hard work to make managing and publishing a website from Obsidian super nice with his project:

obsidian-digital-garden-screenshot.png

How Does it Work?

The setup instructions can be found here: https://dg-docs.ole.dev/getting-started/01-getting-started/, but essentially you:

  1. Install the Digital Garden obsidian plugin
  2. Create a GitHub repo to store the site contents
  3. Authenticate the plugin via a GitHub personal access token
  4. Configure Vercel to build/deploy the site
  5. Add dg-publish: true to the frontmatter of any Obsidian note you want to publish
  6. Use the Obsidian command pallet to publish one (or all) specified notes

new-site-setup.png

The magic happens with:

  1. The Plugin:
    1. Determining which notes + images need to be published
    2. Generating the appropriate file structure expected by the Eleventy configuration
    3. Automatically handling the git operations to commit and push those
  2. Vercel:
    1. Building each commit with Eleventy
    2. Deploying each commit

My New Workflow

I have a dedicated Obsidian vault for this website. It is synced across computers and mobile via iCloud. The daily note is configured to use the following template:

---
dg-publish: false
dg-pinned: false
dg-hide: false
tags: 
dg-published-at: {{date}}
title: foo bar baz
---

and is created at: /Posts/YYYY/MM/DD.

So, to create a post:

  1. Use the Obsidian "Daily Notes: Open today's daily note" command
  2. Update the note name + title
  3. Write the actual post 😅
  4. Toggle dg-publish: true
  5. Use the Obsidian "Digital Garden: Publish Multiple Notes" command

...about 30 seconds later the post is live!

For images, I paste them into obsidian directly. The initially are stored in /attachments with an autogenerated name like Pasted Image 20250709113743_986, but I usually move them to a directory alongside the post note and rename to something meaningful.

Porting Everything Over

In order to make the switch, I needed the corpus of articles already published migrated over.

Luckily, Hugo mostly uses vanilla markdown for authoring posts, so I was able to copy everything over and make a few minor tweaks to:

  1. Images: I had custom Hugo components for handling images (and images that are also links).
    {{< img-link "path-to-image.png" "https://some.link" "Some caption" >}}
    
    I updated these to be:
    [![[path-to-image.png]]](https://some.link)
    *Some caption*
    
  2. Links: In Hugo, default markdown links opened within the same tab so I had a custom link component to open links in new tabs.
    {{< link "https://some.link" "Displayed text" >}}
    
    I updated these to be:
    %% External Links %%
    [Displayed text](https://some.link)
    
    %% Internal Links %%
    [some-internal-page|Displayed text]
    

I could have scripted this, but the number of pages was such that I just converted these manually. Let me know if you notice an image or link that seems broken! 🙏

Javascript sorting woes

Everything was looking great, when I realized that the sorting of posts was wonky.

broken-javascript-sorting.png
10, 11, 02, 03,... WTH?

When constructing my directories (and permalinks), I had used left padded zeros to ensure that the months would sort correctly numerically AND alphabetically.

So what is going on here? The digital garden site has a method which sorts these recursively which can be found here: filetreeUtils.js

This CORRECTLY sorts the months as you would expect. However, this sorted tree is stored as a normal Javascript object which gets rendered by a Nunjucks template here: filetree.njk

This traverses the keys of the Javascript object, resulting in:

Given that, the ES6 Rules specify:

(See https://2ality.com/2015/10/property-traversal-order-es6.html for more detail on what "Integer Indices" actually are)

We end up with this incorrect ordering! 🤬

My solution was to strip the leading zeros when constructing the key:

folders = note.filePathStem
	.split("notes/")[1]
	.split("/")
	// Here we strip leading zeros on the month folder to avoid JS treating 10, 11, 12 as integers,
	// but 01, 02, 03 as strings and sorting incorrectly when displaying with nunjucks. This allows us to
	// keep the leading zeros so that posts from our old hugo site all have the same URL structure
	.map((str) => str.replace(/^0+(?=\d)/, ''));
}

This gives me the sorting I want, while maintaining the URL structure from my old site (with leading zeros) to avoid breaking old links.

Isn't Vercel overengineered for a static site?

The site is built with Eleventy, which generates a static site that could be hosted in many ways (including the old GCS + Cloudflare setup I had before). I could totally update my old GitHub action to use Eleventy (instead of Hugo) to build the new site and deploy to GCS as I was before (and I may do that some day).

For now, I used Vercel because the Obsidian Digital Garden project has a "Deploy to Vercel" button which creates the repo and wires up the necessary configurations. This enabled me to get up and running in just a few minutes! I will monitor if anything ends up costing me (e.g. Vercel's Image optimization or total volume of traffic served), but for now I'm happy with the setup.

Speedrun a New Post

Okay, let's now test how long it takes me to create a new post.

new-post-timer.png
34 seconds!

This is a test to see how fast I can create a post

Will this revamp actually inspire me to write more often? I'm hopeful, but only time will tell... 🤞