Hugo: Happy Accidents & Hard Truths - Part 1
“We don’t make mistakes, just happy little accidents” - Bob Ross
I think the above quote perfectly encapsulates my journey thus far with Hugo , and much of it has been a happy accident rather than the original direction I planned to take when I started mapping out the path for my business.
This blog series with Hugo represents what Granite Dog Systems is all about : doing the research, breaking things, and fixing them properly before delivering a package you can manage once I deliver and train you on its use. I’m figuring out the technical hurdles now so that when you’re ready to scale, the path is better defined.
The Research
Several weeks ago, I started thinking about how I wanted to host my website and what it should contain. Much of the advice I had been reading was to write about my journeys through the many platforms I’ll be using.
Currently, I have my photography site running on Squarespace , which also supports having a blog. But then I realized I would need to pay a monthly or yearly subscription to Squarespace. The other alternative is building a server that would require hosting something like WordPress , for example, but then I would need to address every new vulnerability, including all the add-ons. I have learned through my 25+ year career that every piece of software/platform has a tax: time.
I need to be free to work on client engagements and avoid the time tax of web server software updates. Spending money on third-party hosting solutions doesn’t appeal to me either. Sure, a solution like Squarespace has its appeal, but the cost and the need to learn a limited-use proprietary system isn’t appealing, and I wanted a solution that was a bit more sovereign.
Also, a solution like Squarespace (this isn’t a bash against them; again, I happily use them for my photography site) doesn’t provide enough automation for me. I dream of a day when I can write a blog post, and then a piece of automation wakes up and handles some of the marketing for me, like posting on LinkedIn or Medium.
Why Hugo and the GenX Appeal
As I continued researching, I came across Hugo, which, in some ways, feels very much like a return to the old days before systems like WordPress and other CMS solutions became so popular, and website management and coding were abstracted from the user.
What exactly is Hugo, and why does it appeal to a GenX’er like me? Hugo, at its core, is a static site generator. This whole post is living in a Markdown (.md) file with no HTML. Writing a post follows typical Markdown conventions for links, bold, headings, etc.
You do run a server locally on your laptop that monitors for new files and other changes, then generates those pages on the fly for you to see in a browser.
Along with the core content I am writing, every markdown page includes a section at the beginning called Front Matter that provides Hugo instructions on how to handle the markdown file and any extra steps it may require before they are generated. Below is an example of such a block from a previous post of mine:
---
title: I Spent the Day Saving Five Minutes
date: 2026-02-20T21:25:35.039Z
draft: false
showAuthor: true
showDate: true
description: My time is money and I didn't plan to spend the whole day on this. How I spent my Friday building a script managing CloudFormation so I can stop copy-pasting.
image: images/future-desktop-retro-screen.jpg
---
As you can see, you can add things like the author, date, and a few other bits to give this post a more blog-like feel, if desired. But one might ask, how am I hosting this if I don’t run a traditional server?
As it stands today, this site lives in an S3 bucket on AWS and uses CloudFront to cache and serve the content worldwide. By following this architecture, I can survive regional AWS outages and serve my content more quickly and reliably. The solution is probably overkill for such a small blog, but by using off-the-shelf components, I can build a cost-effective, resilient system.
Challenges and Second-Guessing
The learning and getting up to speed with Hugo wasn’t intuitive. Unlike some of the major CMS solutions, where you can find a book or millions of YouTube videos on setup or troubleshooting, the Hugo ecosystem is a bit smaller.
One of the first challenges I faced was figuring out the look and feel of my website. At first, I landed on a free Hugo theme called Blowfish, and frankly, I wasn’t a huge fan. To be honest, I was so disappointed that I stepped away from Hugo and started second-guessing my decision.
While away from Hugo and doing a bit of research, I learned that you can purchase custom-made themes. I ended up purchasing a theme called Aditu, but I learned it was outdated. Since I am a Platform Consultant, not a professional web designer, I used AI to quickly modernize the theme’s CSS to support newer standards.
Beyond some phone UI quirks that needed to be resolved, jQuery was several versions out of date. It was at 3.5 if my memory serves me right; I am now at 3.7.1. Updating required it to be updated and debugged, along with some 3rd-party scripts the theme used.
Before anyone asks, yes, I avoided the jQuery 4.x train for now. I am a strong believer in avoiding bleeding-edge releases till I start seeing major updates. If jQuery were on a 4.1 release, I would have spent the time.
Along with UI fixes and scripting updates, the theme was pointing to an old, defunct website. So I ended up pulling all of that out, downloading the fonts it required locally so I could have a nice, self-contained, sovereign solution and a bit more reliability. No longer did I need to worry about reference websites that could break mysite.
Another bit of learning with Hugo: it uses a config file called “ hugo.toml ”, which provides more information about how to generate static pages, where the theme is, etc. For example, to define the top-level menu, you need to define it something like this:
# Main Menu
[menu]
[[menu.main]]
identifier = "home"
name = "Home"
url = "/"
weight = 1
[[menu.main]]
identifier = "blog"
name = "Blog"
url = "/blog"
weight = 2
[[menu.main]]
identifier = "about"
name = "About"
url = "/about"
weight = 3
The weights in the code reference tell Hugo how to order the top-level menu.
Another gotcha, you cannot simply go to /about and have it work. This required a CloudFront function to handle this better.
// Added to for hugo to ensure stuff like /about will work.
if (uri.endsWith('/')) {
request.uri += 'index.html';
} else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
Speaking of functions, I didn’t want people landing on my CloudFront URL and living there, so another function was written to help force people to my core website:
if (host.includes('cloudfront.net')) {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
"location": { "value": "https://www.granitedogsystems.com" + request.uri }
}
};
}
Even as I am writing this blog, I decided to create an alias to make it a bit easier to start Hugo with a simplified command. FYI, the code snippet below is specific to a Mac; your mileage may vary, so replace en0 with your specific interface name.
# Start Hugo
hugo-server() {
# Grab the local IP for the Wi-Fi interface (en0)
LOCAL_IP=$(ipconfig getifaddr en0)
echo "Starting Hugo at http://$LOCAL_IP:1313"
# Runs hugo with my laptop IP
hugo server --bind 0.0.0.0 --baseURL "http://$LOCAL_IP:1313"
}
Why or Why Not Hugo
Like any solution, it has its pros and cons. Hugo is no exception to this rule.
The pros of Hugo
- Performance: It is lightweight and quick since the static pages are already generated.
- Security: Smaller attack surface, no more dealing with things like CMS dependency hell.
- Efficient: Once you learn the basics, creating webpages is very quick
- Design: Hugo supports all of your CSS files to make very beautiful websites.
- Optimization: Handy tools like automatic compression of JPG images to WebP files (once you know the trick).
Yes, Hugo has a set of negatives
- Developer-oriented: For developers working with Git, working with Markdown is straightforward; others not familiar with it may struggle, seeing this as a whole new language.
- Niche: An example is limited theme packages for sale vs the major players, and those themes you find might have been sitting for a while and abandoned
- Infrastructure Overhead: It does require some knowledge of a cloud provider to get it running.
Image Compression Automation
It would be rude of me to leave you hanging on the trick for auto-generating WebP files.
First, create an images folder in the assets folder. Though probably not strictly necessary, it keeps your assets directory clean, organized, and manageable.
Next, just reference the image file as you normally would in any post. See my code example below for a reference example:
image: images/blog_hero_post_starting_at_the_beginning.jpg
Once Hugo processes those files, this is what appears in the source code of your public website. Notice the hash, which is part of the Hugo magic.
<img class=post-image src=/images/blog_hero_post_starting_at_the_beginning_hu_d86f2f1327587d39.webp alt="Starting at the Beginning">
This is one of many ways Hugo tries to make your website faster. In this case, you can upload your nice, high-resolution image and let Hugo handle the rest. If you want to read more, you can get some more behind-the-scenes details here.
If you would prefer to use your high-quality images, or if you already done your own image compression, you can use the static folder in Hugo. Once in the static folder, Hugo will use the actual image file and take no extra action.
The following source formats can be processed and converted:
- JPG
- PNG
- TIFF
- BMP
- GIF (Note: Hugo supports processing GIF files, but standard image processing might not support animated GIFs for conversion to WebP)
- WebP (You can also re-process existing WebP files for resizing or quality adjustments)
What is Next for me?
This is only the first of several posts I plan to write. As it stands today, I have basic monitoring in place to alert me to my site’s health, but this is only the beginning of the journey.
- Metrics Oddness: Some of the metrics I am seeing today are curious, including the number of 4xx errors, which require more digging. My website is so small, they shouldn’t be occurring.
- Health Overview: Build a health dashboard that gives me a holistic view of how my site is running on AWS with CloudFront and S3.
- General Hardening: Finishing my WAF setup is a priority and will incur additional costs, but it’s mandatory to slow down the bots and provide greater peace of mind. I do have some geo restrictions in place as part of the CloudFront distribution, but that is no substitute for a good security posture.
- Friendly Editor: To make writing blog posts more user-friendly, I need something that abstracts some of the webpage coding into a more user-friendly frontend. TinaCMS seems to have a lot of promise that I’ll dive into further.
- True Cost: By the time I am done with all the bits, I’d like to see a true cost breakdown of what it takes to run Hugo on AWS. This will require some tagging in AWS and likely some other tweaks to make the data easier to digest.
- Pipeline Refinement: I built a CodeBuild Pipeline to make the S3-to-CloudFront integration smoother, but it still needs a bit of fine-tuning.
In the end, Hugo just gives me more control, and I feel more at home with Hugo, a bit like that cozy flannel shirt in my closet.
Seriously, why?
Some might ask: Why did you post this then, if you have so much work to do? Won’t that hurt your business?
To me, that is part of the journey with any new product or solution, and I refuse to run from talking about the learnings. One of the things I am known for, I am not afraid of giving honest feedback about something. But before doing so, I prefer to have all my eggs in one basket.
I’d rather be honest about what it took to get something rock solid. Getting anything production-ready takes time, sweat, swearing, you name it. This process doesn’t happen overnight.
“Don’t rush me, sonny. You rush a miracle man, you get rotten miracles.” - Miracle Max, The Princess Bride
In future installments of this series, I’ll be diving deeper into the topics I need to research and refine, including health, cost, security, and general refinement.
