Skip to main content
  1. Blog/

Migrating from Jekyll to Hugo

It's been ten years. It's time for a change.| ·653 words
Andy Jackson
Andy Jackson
Fighting entropy since 1993

About ten years ago I realised that, for a personal website, Drupal was far too much work to maintain. So I started migrating that website to a static site system, specifically Jekyll. This ran on GitHub Pages at first, and moved to Netlify later on. Looking back, I’m surprised I didn’t blog about that in more detail. But perhaps it was because I never exactly finished the Drupal-to-Jekyll content migration!

But now, as I’m about to move jobs, I want to simplify and re-focus this site to better match where I’m headed. I also want to move from Jekyll to the Hugo static site engine, as it’s really fast and I’m more familiar with it than Jekyll at this point. It was during this process that I realised the original Drupal-to-Jekyll migration had broken quite a few things, so I’ve taken the opportunity to patch up some of the gaps.

Thankfully, these gaps were mostly not around the maintaining the URLs, which I think I’ve managed reasonably well during these transitions. Static site engines usually support generating web pages from simple Markdown files, with URLs based on file names and paths, which makes them nice and explicit, and most support redirecting users from older URL aliases. Because of this, the Jekyll-to-Hugo mapping was much simpler than the original shift from Drupal.

However, there are some differences in conventions, especially around the metadata declared at the start of each pages (a.k.a. frontmatter). In some cases, these can be dealt with by modifying the Hugo configuration or templates, but in other cases it’s easier to update the metadata itself. Apart from anything else, keeping the conventions close to those usually used by the static site generator means most of the existing themes and templates should work immediately.

Updating this embedded metadata may sound daunting, but the available tools these days are so good. A little searching soon led me to examples of using a tool called yq to do exactly this kind of thing. For example, this little script takes a set of Markdown files, and updates three metadata fields, e.g. using aliases instead of redirect_from to define the alternative URLs for a page:


for arg in $@; do
    echo "Updating: $arg"
    yq -i -f "process" ".url = .permalink | del(.permalink)" $arg
    yq -i -f "process" ".aliases = .redirect_from | del(.redirect_from)" $arg
    yq -i -f "process" ".date = .created" $arg

But in other cases, configuration is a better tactic. For example, under Jekyll, the RSS feed for my site is at By default, Hugo uses index.xml, but some investigation showed there was a way to handle this via Hugo’s configuration options, so I can maintain that URL by tweaking these options:

        baseName = "feed"

The biggest problems from the original Drupal-to-Jekyll migration were around embedded resources, like the fairly complicated way Drupal connected images embedded in pages with the ’nodes’ that define any embeddable images. This was a bit more complicated, requiring a custom template for image pages and a shortcode helper for re-using images in other pages.

The custom template is the most cumbersome part of this, as tweaking templates necessarily depends on the theme that you’re using. I’d already chosen Blowfish by this point, which already supported most of what I wanted, and provided a strong basis for minor modifications. For example, Blowfish has simple hooks for adding things like extra JavaScript, which I’m using to switch away from Google Analytics (I’m going to try Umami for a while).

With all these changes in place, I’ve mostly been tweaking how some parts are presented, e.g. the Projects, and flicking through various pages to look for problems (like fixing broken images in this surprisingly popular piece of chocolate history). There’s more to do, in particular the tags and categories are a bit of a mess, but I think things are good enough to go live.

So here… we… go…