Save Money by Migrating from Ghost on AWS EC2 to Hugo on AWS S3

webdev cloud

How do you migrate 2 years of your life in 2 days? And why does it take longer to migrate from Ghost to Hugo?

Migrating anything is a massive undertaking. Back in March 2020, there were rumours that the Japanese government was under-reporting Covid cases. Faced with the threat of being stuck in Tokyo, a prospect I would gladly accept under any other circumstance, I scrambled as Tokyoites do in the Strong-stenched Shibuya twilight.

My flight had been brought forward. That gave me 2 days to pack and exit my cheap and cheerful sharehouse. Mercifully, a good friend who hadn’t yet decided to flee volunteered to help bring my shipments-to-be to Ta-Q-bin. We stumbled down the streets of Okachimachi, hauling 20kg of stuff. It was just down the street, I said.

Onto the next character-building migration.

This personal landing page was first moved from traditional web hosting to a EC2 micro instance, because free stuff must take. It became the sort of thing you create with explosive enthusiasm and sort of unwittingly neglect later.

In the midst of doing something wholly unrelated, I abruptly realised that AWS' free tier expires within a year of account creation. No more free t2.micro compute! The horror 👿

It was time to migrate again to something that was less expensive to forget about. Something minifiable. Why pay dollars when you can pay $0.000001s of dollars?

The ghostToHugo scare

I started out using this brilliant thing which promised to get my Ghost blog into Hugo in a hurry, just to see if it would be easy before I got going. It was not.

Ghost image-caption pairs had turned into Hugo shortcodes that couldn’t render. HTML tables were not converted into markdown tables. References and end-notes didn’t line-break at the right places. Not finding any further patterns where these happened, I made the changes manually.

To its credit, the markdown and frontmatter was otherwise faithful. A nice time-saver if you didn’t do anything too complicated in Ghost.

Not everyone wants to read in Dracula

It takes maybe half a minute to get the Hugo site itself up and running. I spent more time deliberating if it would be ok to let Dracula take over my website, too, than downloading and installing the thing.

I referred to one barebones tutorial to pick up the syntax used by Hugo in views, and a little bit about the expected directory structure — thank you Pedro! Bootstrap meant time was spent choosing colours and figuring out how to get Hugo to do SASS. You could pick it up in an hour.

After a few more hours of testing trending vim palettes, I decided that they were great for writing code but terrible for reading prose.

Using CloudFront with subdirectories

Hooking Hugo up to S3 and CloudFront is just a matter of creating the relevant resources, configuring the AWS CLI with your credentials, running hugo to generate the static files, then running hugo deploy with the bucket address stated as a parameter.

S3 is a key-value store with a HTTP endpoint, which works beautifully for single-page websites. However, S3 gets lost when you try to navigate to a subdirectory, such as, claiming there’s no object there. That’s because the object you need lives at

You’ll need to set up CloudFront with an additional Lambda@Edge (only available in the us-east-1 region) function. Thankfully, AWS provides a boilerplate solution to this.

I’m relieved to have this all up and running. I’ll be monitoring the cost but I’m sure it’ll be massive savings over using a t2.micro instance on EC2 - which is already overkill for my purposes.

I find that any kind of migration also forces you to do a Marie Kondo on your dependencies and organisational habits, and hopefully learn something new in the process, like I did here.