Skip to main content
  1. Writing/

Hosting Your Hugo Blog For Free

·757 words
Table of Contents

I love infrastructure and anything that has to do with it. That is - I love tinkering with different services and see what I can do to make them play nice with each other. I also like being efficient with what and how I deploy, and recently something dawned on my - I am spending way too much money on hosting my personal blog, that is 90% text.

A recent analysis brought me to the number: 30$/mo. And before you ask, yes, I used shared hosting before, and no, shared hosting is not the solution. Most shared hosting services are actually really low-scale versions of what I needed. I am by no means operating at the scale of Daring Fireball, but I also got to the point where limited bandwidth email notifications were sent my way.

I switched some infrastructure around, that gave me plenty of flexibility and even put my blog behind a CDN (yay bandwidth savings). However, the cost was somewhat high for what I was doing, so I thought I would figure out a better way, and put $30/mo to putting gas in my car. Or getting more Starbucks - really, whichever way works best that particular month.

Enter GitHub Pages - a free way to host static sites directly out of a GitHub repository! You can’t beat free, so I thought I would look closer into what I can do here. Reading all the guidelines, I originally got the impression that the site hosted this way on GitHub has to be Jekyll-based. That worried me a bit because I didn’t want to move away from Hugo. After some research, however, I stumbled across a guide (official one, at that) that shows how to host a Hugo site on GitHub Pages! Jackpot.

The problem with the instructions above is that they require some manual work - the assumption is that the site owner would effectively act as the content generation trigger - you write things locally, you generate the HTML on your machine, and then push that to a GitHub Pages-provisioned repository. What if we can automate this? And with GitHub Actions, that is now really easy! No, really - I am using the word “easy” here literally.

The setup #

I split my workflow in two repositories:

  • Content repo. Houses the Markdown files and the Hugo configuration.
  • Publishing repo. Houses the Hugo-produced HTML. A human never touches this repo.

To set up the publishing repository, I followed GitHub’s own instructions. Within the content repository, however, I’ve set up a GitHub action, that I am pulling from dend/actions. The action itself does the following (defined in entrypoint.sh):

#!/bin/bash -l

apt-get update
apt-get install -y wget git

CURRENT_BRANCH=$(git branch | grep \* | cut -d ' ' -f2)

wget $HUGO_URL

yes | dpkg -i hugo*.deb

hugo version

# Make sure we have the latest theme.
git clone --progress --verbose https://$BLOG_DEPLOY_KEY@$LOCAL_THEME_GIT_URL $LOCAL_THEME_LOCATION
git clone --progress --verbose https://$BLOG_DEPLOY_KEY@$BLOG_PUBLISH_URL $BLOG_PUBLISH_LOCATION


cd $BLOG_FOLDER
hugo -v

if [ "$CURRENT_BRANCH" = "master" ]; 
then
  git config --global user.email "$GIT_EMAIL"
  git config --global user.name "$GIT_NAME"

  rm -rf $BLOG_PUBLISH_LOCATION/*
  cp -a public/. ../$BLOG_PUBLISH_LOCATION/
  cd ../$BLOG_PUBLISH_LOCATION
  git add .
  git commit -m "Update content."
  git push
else
  echo "Not master - we don't need a deployment, just validated the content."
fi

git branch

echo "Current branch:"
echo $CURRENT_BRANCH

This script does the following:

  1. Updates the locally-installed packages.
  2. Installs wget and git (helpful to have when we have to download content from the web and a Git repo).
  3. Get the current repository branch to determine if we need to execute publishing (only done against changes in master).
  4. Clones the theme folder (separate repo, because you should always separate concerns).
  5. Clones the publishing repository.
  6. Builds the content from the current repo (effectively, translate Markdown to HTML).
  7. Clear the publish repo.
  8. Copy the newly-generated content into the local folder where the publishing repo is.
  9. Commit and push the changes.

The output #

Once the action runs, it replaces the content in the publishing repository with the content that was just generated. That is then pushed through GitHub’s static site publishing process, and you will get an email notification when the build completes. Once the build is done, you can navigate to the site URL (I set up a custom domain) and see the fresh content light up!

The cost #

The cost went down to effectively the monthly GitHub subscription ($7/mo) because I want to keep the blog repos private, and the cost of the domain ($10/yr). Huge cost savings, much more efficient publishing and content management.