Migrating my Blog from Ghost to Hugo

Wednesday, July 1, 2020

The Story

Like any hipster developer, I got caught up hard in the node.js craze circa 2015 and when go started gaining traction, heck I jumped over there too. I first started my blog in an attempt to ‘practice what I preach’ to my peers and young minds that I was hoping to help mould for the best. I think that every developer should have their own blog. Not in a vain attempt to get clicks or ad revenue, but to keep a log of that tough-to-diagnose error that they tried for weeks to diagnose and fix. My blog acts a lot as a personal reference for issues I’ve faced in my coding life and what I did to get around them.

In a silly attempt to get approved for google ads, I deleted several very helpful-for-me posts that google flagged as being too short to allow me to enable ads on my site for. So I deleted them. This is the only real regret I have in my blogging life.


That’s why I blog. This is also partially the reason why I’ve switched from Ghost to Hugo. I was hoping to set up google ads on my blog to help pay for my hosting costs. Since I couldn’t get approved for ads, I started exploring different paths.

Github Pages

Github Pages is an offering that allows you to publish a website using git. The name pretty much says it all. The best part: It’s free! The genearl idea is:

  1. Create a new repository that looks like [yourusername].github.io
  2. Clone the repo
  3. Add an index.html file with some content
  4. Commit and push
  5. Navigate to [yourusername].github.io and boom, you have a website!

Based on my understanding, github pages does a great job at hosting up static html files. Since my current blog runs on Ghost which is a client/server architecture, I would need to migrate my blog to a static site generator so I can make use of Github Pages.

Since I have fallen in lust with go, and Hugo is, by their own definition

The world’s fastest framework for building websites

I decided to give it a go!

Setting up Hugo

Since I use my macbook for everything non work-related, I used homebrew to install hugo. There are several installation options located on hugo’s official getting started pages but I elected to use the trusty homebrew option.

brew install hugo

Once that’s all done, you can create a new site by running

hugo new site myepicblog

Next you should initialize your git repo and add a theme. I’m using the theme hugo-notepadium. You can either clone the themes and commit them with your code or use the snazzy git submodules to load it up during your build phase. I elect to load it during the build phase.

cd myepicblog
git submodule add https://github.com/cntrump/hugo-notepadium.git themes/hugo-notepadium

You can see what you’ve got happening by running hugo server and navigating to localhost:1313 You should see a static site and some content. Sweet! Our Hugo site is running.

Migrating content

I started to cringe at the idea of manually moving my content from Ghost to Hugo. Ghost’s content is stored as markdown Hugo’s page setup is, as expected, all stored in Markdown while Ghost relies on a database to organize content. Luckily the community came to the rescue! Ghost provides a list of tools developed by the community to help you migrate from different engines to hugo. Luckily, ghostToHugo was there to save my day.

Snag a backup of your ghost blog

Log in to the admin portion of your ghost blog and perform a backup. this creates a .json file with all of your blog information. You can find detailed instructions over at ghostforbeginners.com. Save the export.json somewhere accessible!

Migrate content

Go ahead and install ghostToHugo as mentioned above from here. There are many options to install it but I chose to use the go tooling for it. Go ahead and open a terminal and type go get -u github.com/jbarone/ghostToHugo

Now it’s as easy as running ghostToHugo export.json. I had to play with the dateformat a bit as my blog apparently didn’t use the default, but you can figure that out if your blog is different. You can configure this with the -d flag in ghostToHugo This will create a new hugo site and will put all of your ghost posts in the content folder. Copy these to your new content folder from the previous site setup and you could be good to go from here.

Personally, I wanted a bit more organization in my posts. I don’t have a lot, but I have enough where a flat structure wasn’t working for me. Spend some time here organizing your posts structure for your own sanity down the line. I chose to organize my posts with a content/posts/:year/:slug.md structure.

Hugo is probably refreshing itself so you can see your posts showing up at localhost:1313 as long as your server is still running

For backwards-compatibility…

I have literally one post that anyone ever finds on google. It’s my post on react and indexeddb and it generates an ok number of clicks a week so I don’t want it to get de-ranked. My ghost blog had a very simple permalink setup: /:slug. Luckily, in ghost I can configure the same thing! In config.toml I added

  posts = "/:slug/"

and just like that, my posts are all configured the exact same as my old ghost blog. You can play around with that to fit your own use-case.

Now What?

You could be hacky and just initialize your github repo in the public/* folder, add the contents and push… but we’re devs and we like to make things automated and slick.

GitHub setup

We are going to need…

  1. a repo for your blog’s source control. I chose github.com/jimdhughes/blog (it’s private)
  2. a repo for your published site. I chose github.com/jimdhughes/jimdhughes.github.io
    • the .github.io is very important.
  3. A secret Token that gives you repo permissions in your account. This will need to be added to the repo identified by #1
  4. a Github action to build and deploy your static site :)

Let’s do it!

  1. Steps 1 and two are pretty straight forward. Add your hugo site to the repo in #1 and leave the repo from #2 empty.

  2. Create a new personal access token here: https://github.com/settings/tokens/new with repo control. It’s going to give you a token. You need to copy this as you’re going to set it up in your project repo.

  3. In your project repo, add a ‘secret’ in github.com/{yourusername}/{yourrepo}/settings/security/secrets called ‘TOKEN’ and paste the value from step 2

  4. Add a build.yml file in .github/workflows

name: CI
on: push
    runs-on: ubuntu-18.04
      - name: Git checkout
        uses: actions/checkout@v2

      - name: Update theme
        run: git submodule update --init --recursive

      - name: Setup hugo
        uses: peaceiris/actions-hugo@v2
          hugo-version: "0.64.0"

      - name: Build
        run: hugo --minify

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
          personal_token: ${{ secrets.TOKEN }}
          external_repository: <YOURACCOUNT>/<YOURGITHUBIOREPO>
          publish_dir: ./public
          user_name: <YOURUSERNAME>
          user_email: <YOUREMAIL>
          publish_branch: master

Make sure to replace all the in the above yml file with actual info. You can see we make use of a few github actions by peaceiris and that token we created gets used here to publish your page.

Commit all this code and push it to master and then watch the magic happen :) An action will be triggered which will build your hugo static site and push all the assets to your .github.io repository. Navigate to .github.io and you can see the delight that is your static website!

Adding a custom domain

The last step in my migration was to add a custom domain to github pages. To do this you need to do a couple things.

  1. Log in to your DNS and remove the A Record for your subdomain and add a CNAME record of .github.io
  2. Once the DNS has propagated, go to the repo of your .github.io
    1. Press Settings
    2. go down to Github Pages
    3. under custom domain, add the name of your domain (mine was blog.jimdhughes.com)
  3. Once github finds the cname reference in your DNS, it will generate SSL certs. I elected to ‘enforce SSL’ because I think it’s good practice. This will redirect all http requests to your blog to https.

To ensure that the CNAME takes on each deploy, add a static file called CNAME to the static folder of your repo and put the cname you want in there. For instance, my static/CNAME folder has blog.jimdhughes.com saved in it.

That’s it! A bit of effort and I’ve made my blog a little simpler and saved myself $5 USD / month (which is like $10000 CDN/mo) and I think it feels just a little bit snappier.


BlogGolangHugoGhostGithub Pages

Docker Seq Setup With API Key

NASA has an API!

comments powered by Disqus