Per Ardua Ad Astro
I have moved my Website from Hugo to Astro, 4 years after a migration from Wordpress. This article describes the experience. Short version: I am pleased with the result, after 4 years of frustration I can finally get things working how I want them to on the deployed site.
Background - Hugo = Huge Work
Four years ago I migrated this website from Wordpress to Hugo. It was a great improvement for my use case but as I noted at the time, I needed individual assistance to get it working and:
I initially thought that the Hugo part of this implementation would IMO be straightforward enough for someone who would consider a third-party hosted Wordpress solution but trying to move beyond the raw theme to integrate e.g. site search and custom icons certainly requires a great deal more understanding of templating and CSS than WordPress!
Four years later it hadn’t got any easier or more intuitive. The theming support is notional- you’re expected to clone a git submodule into your repo - so that the code is both ‘there’ and ‘not there’ depending on your perspective. Either way you’re pinned to a specific commit hash, which is fine, until something breaks, and there’s no upgrade mechanism… In my case the theme I had chosen was discontinued by the original author, someone else created a fork but their support was spotty which meant that I wound up forking that… Parts of the site would randomly break on upgrades, e.g. Hugo v0.156 removed .Site.Author and broke overrides so it’s not like I could just ‘not update’. Trying to customise even ‘simple’ things like the size of top level titles was essentially impossible for me and the hierarchy of partials and Go Templating to edit it never really made sense to me, even after another 18 months of working with Helm and Crossplane in the day job. I was always forced to revert changes and dragged back to the theme defaults. There is no more Go coding with Hugo than there is with Terraform or with Helm, just an awkward templating framework with no tradeoff.
Meantime I had been doing increasingly full stack development, especially with Web apps as shown on this site. It was increasingly frustrating not being able to adjust the appearance of my site in a meaningful way. It was time for a change.
Evaluating The Alternatives
I wanted a new static site generator that was popular and well supported. For me it came down to Eleventy or Astro. The numbers (Early August 2026) were overwhelmingly in Astro’s favour:
| Generator | Created | GitHub stars | Weekly npm downloads |
|---|---|---|---|
| Hugo | 2013 | ~89.3k | n/a (Go binary, not npm-distributed) |
| Eleventy (11ty) | 2017 | ~19.8k | ~170k |
| Astro | 2021 | ~61.5k | ~2.35M |
Astro’s showcase includes IKEA, Porsche, Unilever, Microsoft, The Guardian; 11ty’s showcase skews toward personal/small-org sites. As a side point, for both Eleventy and Astro you are expected to essentially build or customise your own theme, e.g. Astro offers a one-time scaffold. After the Hugo experience this actually seems like an advantage! The clincher was Googling ‘migrating from hugo’ was all results mentioning Astro.
Clauding It And Correcting It
I decided to go with something broadly similar in appearance to my existing Hugo site - obviously a big driver for the migration was frustration with the current rigidity of the theming so it wasn’t like I really wanted a precise match. I decided to set up a completely separate deployment for a beta site during development and did a fresh initialisation there.
The initial setup was fairly quick but the yak shaving took much longer. There were various foibles to accommodate to match the existing page URLs and accommodate Cloudfront conventions. Something that I have seen before with Claude (Code) 5 Sonnet but had not initially thought to check for explicitly popped up- a custom solution when something is already available off the peg. In this case it was as I was trying to implement article previews - automatically displaying the first image and paragraph of an article. It became apparent that Claude had decided to build its own markdown renderer based around regexs… An obvious wrong decision? Sure enough Astro already has two markdown renderers which are officially supported- I went with unified/remark/rehype and then decided to hunt for other handrolled solutions where there was a sensible one available off the peg. Sure enough, there was a hand-rolled slugify() for TOC anchor IDs on the Web Applications page, while Astro’s own heading-slug system already uses github-slugger internally. I swapped to the same package for consistency, added as an explicit dependency. There was also a ‘custom’ RSS implementation which needed immediate retirement in favour of @astrojs/rss and @astrojs/sitemap… The greatest difficulty was (re)implementing Pagefind (site search) to look sensible with a responsive design.
Deployment and Cutover
Hugo has a nice integration for deployment, Astro doesn’t. For reference this site is AWS CloudFront/S3. Claude suggested a custom script, which was fine. The Upstream AWS distribution docs from Astro are not great in my opinion. They suggest:
- AWS Amplify - ok, I guess
- S3 static website hosting. You should not be recommending public S3 bucket access in 2026
- S3 with CloudFront - refers to the above public access, sad times.
- Continuous deployment with GitHub Actions - you should not be recommending static AWS credentials in 2026, you should be using OIDC
Because static site it was simple to deploy a second bucket and subdomain for a beta deployment of the Astro site alongside the Hugo one. I could then check everything side by side before cutover- all articles, URLs, features, etc, even byte match articles, RSS, automated TOCs.
Because the DNS, distribution etc is all managed at an infra level I simply needed to ‘deploy’ (delete all bucket contents, deploy new content, invalidate CloudFront cache). This also allowed me to a do a dry run and comparison which caught that I had not got a 404 error page before going live.
After going live, I wished I had prepared the browser cache control better in advance. The TTL was set to an hour and the page links were the same so there were pahntom 404’s until I cleared my local browser cache I could have simply redeployed the Hugo site, updated there, waited and then redeployed the Astro one but since it was only an hour anyway I didn’t bother, just did a tear down of the beta site.
Final Thoughts
Obviously both Hugo and Astro are static site generators so once set up daily use is more similar than it is different. Both can run a local development server to check things locally, in both cases with an additional build step for PageFind. (Once scripted for Astro) Both have a similar deployment route. Both support the same format for articles with the same Frontmatter.
The difference of course is in the configuration and the theming. The numbers of course speak for themselves- Astro is far more popular- and it’s not hard to see why. Hugo’s whole layouts/partials/shortcodes business coupled with Go templating is a mess. It’s not only complex and idiosyncratic, it simply does not work well. It’s a bit early in my journey to be bigging up Astro but as a comparison- in 4 years with Hugo I hadn’t been able to get my site looking how I wanted it, only how the theme author wanted it. In 4 days I was able to move to Astro and accomplish this with things like expanders using native HTML rather than a special custom layouts/_default/baseof.html with an npm import…
Four years of Hugo showed me what I didn’t want. Four days with Astro got me a lot closer to what I did. Per ardua ad Astro, indeed.
