Hugo suddenly serving only 404s locally while GitHub Pages worked

Trying something new here. I have in this post, asked Chat GPT to turn a full troubleshooting thread into a blog post. I did not edit it–maybe except a tiny one–, because my goal was to document this particular weird problem without really spending time on writing a blogpost. Actually I think the result is pretty good, sticking very nicely to reality. It is not in my voice or my style, but that’s ok, I literally just wanted to document the thing and move on.

Symptoms

I noticed that newly created blog posts weren’t appearing on my local Hugo server at http://localhost:1313.

Oddly, after deploying to GitHub Pages, the exact same posts appeared correctly online.

Initial hypothesis: perhaps Hugo wasn’t noticing new content or was caching old pages.


Discovery #1: I wasn’t even talking to the correct Hugo server

Running hugo serve reported

Web Server is available at http://localhost:51852/

but I had been habitually browsing to

http://localhost:1313

It turned out an old Hugo process had been running for many hours.

lsof -i :1313 -n -P

revealed the stale process.

After killing it, a fresh hugo serve again bound to port 1313.


Discovery #2: Everything returned 404

Unfortunately, even after killing the stale process, every request still returned a 404.

Not just the newest posts.

Every page.

Even pages that were known to exist on GitHub Pages.

Interestingly, curl produced the same result as Chrome and Firefox, ruling out browser cache issues.


Discovery #3: Hugo does not emit HTTP access logs

One debugging instinct was to enable verbose logging.

hugo server --logLevel debug

Surprisingly, Hugo still produced no per-request logging.

This is expected behavior.

Hugo’s debug logging is primarily for the build pipeline, not HTTP access logging.


Discovery #4: Hugo still saw all of my content

Running

hugo list all

showed every expected page.

So this was not a content discovery problem.

The content existed.

The server simply wasn’t rendering it.


Discovery #5: Layout warnings suddenly became suspicious

I had been ignoring warnings like

found no layout file for kind "page"
found no layout file for layout "post"
found no layout file for kind "section"

Originally I assumed they were harmless.

Once every page became a 404, they suddenly became the primary suspect.

If Hugo cannot locate any layouts, it has content but no templates with which to render it.


Discovery #6: GitHub Actions was quietly doing something my laptop wasn’t

The deployment workflow, .github/workflows/gh-pages.yml, contained

submodules: true

which fetches the PaperMod theme during CI.

Locally I checked

git submodule status

and saw

-266553fe... themes/PaperMod

The leading - indicates the submodule has not been initialized.

This was the clue.


Root cause

My local checkout no longer had the PaperMod theme checked out.

GitHub Actions automatically initialized it during deployment.

My local machine did not.

Therefore:

  • GitHub Pages had layouts.
  • Local Hugo had no layouts.
  • Hugo emitted layout warnings.
  • Every request became a 404.

Fix

git submodule update --init --recursive

Immediately afterwards

hugo serve

worked normally again.


Lessons learned

  • Don’t ignore layout warnings from Hugo.
  • git submodule status is worth learning to read.
  • A leading - means the submodule has not been initialized.
  • CI may be masking local problems by automatically fetching submodules.
  • When local and CI disagree, compare the entire build environment, not just the Hugo version.