Tutorial1 day ago

4. Hugo Beat Eleventy at Every Size We Tried, and We Would Still Not Pick It for Everyone

Hugo built our 10,000 article site in 2.45 seconds to Eleventy's 4.8 and won at every size we tried. It also used nearly twice the memory, and 17 of its last 42 releases flagged something breaking, deprecated or removed.

The WJS Desk

Sep 23, 2026 ยท 9 min read

Photo by joao Guerreiro on Pexels

Three parts, 15 things that broke, and one head-to-head later, here is where we landed. Hugo is the fastest tool we ran on our own content at every size we tried, it installs as a single 89.7 MB binary with nothing else to maintain, and it will change under you roughly every three weeks. Whether that trade suits you depends on how big your site is, what format your content is already in, and whether anyone will own upgrades. We answer each one at the end, with the numbers behind it.

To make the verdict mean something, we built the same site a second way. Eleventy is the closest like-for-like alternative we know of: a static site generator with no framework opinions, 19,930 stars, MIT licensed, running on Node. Same 128 articles, same 266 tags paginated at 20, same repeated corpus at 10,000 and 50,000, same machine.

Same data, same machineHugo 0.166.0Eleventy 3.1.6
Install7.86 s, one 89.7 MB binary7.77 s, 129 npm packages, 22 MB
128 articles, wall clock0.11 to 0.16 s0.54 to 0.56 s
10,000 articles, wall clock2.43 to 2.48 s4.58 to 4.97 s
50,000 articles, wall clock16.24 to 16.29 s24.51 to 25.50 s
50,000 articles, peak memory5.0 to 5.2 GB2.7 to 2.8 GB
Hugo with part 2's unfixed templates, 50,00086.84 to 92.64 s(not applicable)
HTML code blocks after migrationIntact via adapter, 8 of 128 damaged via Markdown filesIntact, bodies never touch Markdown

Times are from /usr/bin/time -l on an Apple M4 Pro with 48 GB of RAM, clean output folder every run, two or three runs per cell. Eleventy ran on Node 22.22.0. The two tools did not write identical sites: Hugo also generates category pages, a paginated section list and alias redirects, so at 10,000 articles it wrote 14,146 HTML files to Eleventy's 12,855. That is extra work on Hugo's side, and it still finished first.

Why we ran a second tool at all

A verdict that only measures one tool is a review, and reviews are how people end up adopting things for the wrong reasons. Hugo's pitch is speed. You cannot judge a speed claim without something to measure it against, on the same content, built the same way.

We picked Eleventy over Astro or Next.js because it is the like-for-like choice: both take data and templates and write HTML, neither ships a JavaScript framework to the browser by default. Astro and Next.js solve a bigger problem, and comparing build speed across that gap would flatter Hugo in a way that tells you nothing.

Reproducing the Eleventy side

This needs Node 18 or newer (Eleventy's engines field says >=18) and the two article articles.json from part 2. It is the whole Eleventy version of our site minus tag pages, which took us about 30 more lines that you can skip for a first comparison.

mkdir -p ~/hugo-course/eleventy/src/_data ~/hugo-course/eleventy/src/_includes
cd ~/hugo-course/eleventy
npm init -y
npm install --no-audit --no-fund @11ty/eleventy@3.1.6
cp ~/hugo-course/export/articles.json src/_data/articles.json
cat > src/_includes/base.njk <<'EOF'
<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>{{ title }}</title></head>
<body><main>{{ content | safe }}</main></body>
</html>
EOF
cat > src/article.njk <<'EOF'
---
pagination:
  data: articles
  size: 1
  alias: a
permalink: "/{{ a.slug }}/"
layout: base.njk
eleventyComputed:
  title: "{{ a.title }}"
---
<article><h1>{{ a.title }}</h1>{{ a.content | safe }}</article>
EOF
cat > eleventy.config.mjs <<'EOF'
export default function () { return { dir: { input: "src", output: "_site" } }; }
EOF
npx @11ty/eleventy

On the two article sample it reported Wrote 2 files, and _site/the-trap/index.html had its blank line and shell comment intact. Eleventy's safe filter inserts the HTML as-is, so part 2's code block corruption cannot happen by construction.

What broke in this part

Eleventy's empty project exits 0 too

Part 1 complained that Hugo's empty build exits 0 with no HTML. In fairness, we ran Eleventy in an empty folder. It printed [11ty] Wrote 0 files in 0.01 seconds and exited 0. At least it says "0 files" in the one line it prints, where Hugo buries the problem in two warnings above a table. Neither tool treats "nothing to build" as an error.

Hugo's version guard is a warning

Part 1 showed PaperMod refusing an old Hugo. Your own project can do the same: Hugo lets you declare a minimum version in hugo.toml. We set it to a version that does not exist yet:

cd ~/hugo-course/wjs-adapter
printf '\n[module.hugoVersion]\n  min = "0.170.0"\n' >> hugo.toml
hugo
hugo --panicOnWarning

Hugo printed WARN Module "project" is not compatible with this Hugo version: Min 0.170.0 and then built the site anyway, with exit status 0. Only with --panicOnWarning did it fail (exit 1, failed to load config). This is the same lesson as every other part: in Hugo, warnings carry the information and the exit code does not. Set the minimum to the version you actually need (0.162.0 for the adapter from part 2) before moving on:

sed -i '' 's/min = "0.170.0"/min = "0.162.0"/' hugo.toml
hugo --panicOnWarning --quiet

The cost nobody lists: churn

Hugo is still at version 0.x thirteen years in, and it uses that freedom. We pulled every release from the 12 months to 23 September 2026: 42 releases, and 17 of their release notes contain the words breaking, deprecated, removed or "denied by default". Four of those changes hit us directly in this course:

  • v0.146.0 (April 2025): the template folder layout changed. Themes built for it refuse older Hugo, as PaperMod did in part 1.
  • v0.153.0 (December 2025): macOS downloads became .pkg only. Scripts that fetched the tarball found nothing to fetch for new versions.
  • v0.158.0 (March 2026): .Language.LanguageCode and friends were deprecated. PaperMod, the most popular theme, still printed those warnings in September.
  • v0.162.0 (May 2026): HTML content files were denied by default, which is the security.allowContent error from part 2.

None of these were careless, and the HTML change closed a real XSS route. But they add up to a tool you cannot install and forget. The project is responsive about it: 38 issues were opened and 56 closed in the month before we checked, with 193 still open.

The practical defence is to pin, and to upgrade on purpose:

brew pin hugo
brew list --pinned

We confirmed brew list --pinned showed hugo. We could not demonstrate the pin blocking an upgrade, because 0.166.0 was already the latest release. Combine the pin with [module.hugoVersion] and --panicOnWarning in CI, and a version mismatch becomes a failed build instead of a quietly different site.

Watch out: Hugo's popularity means most of what you will find when you search an error is from older versions. In this course alone, two pieces of widely repeated advice (download the tarball, use .html content files) were wrong for current Hugo. Check the date on anything you copy.

Everything that broke, in one place

PartWhat brokeExit code
1Empty project built no HTML0
1Draft silently skipped0
1Theme demo posts published as ours0
1Sitemap and RSS pointing at example.org0
1Deleted pages still served from public/0
1go install edition could not compile Sass1
1Current theme on Hugo 0.120.01
2JSON front matter inside YAML fences1
2Raw HTML stripped from all 128 articles0
2Code blocks rewritten in 8 articles0
2HTML content denied by the 0.162 policy1
2Every adapter page dated 1 January 00010
3Quadratic home template: 86 s at 50,0000
330 dead internal links, then title-based URLs0
3Future date, duplicate slug, empty title and body0

Four failures were loud and precise, with file, line and column. Eleven built successfully. The CI gate from part 3 catches or prevents six of those eleven (the empty site, example.org, stale files, stripped HTML, dead links and the future date), and it misses the other five. Those need checks on your own data before Hugo runs.

Who should adopt Hugo

Adopt it if your content is Markdown and your site is big or growing. At 10,000 articles Hugo built in half Eleventy's time; at 50,000, two thirds. A single binary with no node_modules is a real maintenance saving, and it matters more the longer the site lives. Documentation sites, long-running blogs and archives are Hugo's home ground.

Adopt it if you will run a CI gate. Every serious problem we hit in three parts either failed loudly or is catchable with --panicOnWarning and 13 lines of shell. Teams that will add those checks get Hugo's speed without most of its surprises.

Adopt it if you are willing to write your own templates. Ours were 29 lines for a full blog. Themes are where most of the version pain lives.

Who should not

If your content is already HTML in a CMS and the site is small, Eleventy is the easier migration. At 128 articles the difference was 0.11 seconds against 0.55, which no human will notice, and Eleventy never put our HTML near a Markdown parser. Hugo's content adapter got there too, but only after the policy error and the silent dates bug.

If your CI runners are small and your site is huge. Hugo peaked at 5.2 GB building 50,000 articles, nearly twice Eleventy's 2.8 GB, and the Hugo dev server reached 4.3 GB at 10,000 after a few edits. On a 7 GB runner the build fits and the margin is thin.

If nobody will own upgrades. Seventeen release notes in a year flagged something breaking, deprecated or removed. A pinned version works indefinitely; an unpinned one on a laptop that runs brew upgrade will eventually meet a theme it disagrees with.

If you need an editing interface for non-technical writers. Hugo has none. We did not test the third-party CMS front ends that add one.

Common mistakes when deciding

  • Comparing Hugo's build speed to a framework like Next.js, which is doing a different job.
  • Judging speed from a 10 page demo. Our first templates were fine at 128 articles and 5x slower than necessary at 50,000.
  • Picking a theme first. The theme decides your upgrade pain more than Hugo does.
  • Assuming the migration is done because the build is green.
  • Following advice without checking which Hugo version it was written for.

What we did not test

We did not deploy either site anywhere, so we have no numbers for Netlify, Cloudflare Pages, GitHub Pages or hugo deploy. We did not test image processing, which dominates many real Hugo builds, or multilingual sites, or Hugo Modules beyond the version check. The large corpora are our 128 articles repeated, not distinct content. Our Eleventy site had no category pages, which favours it slightly. We tested on one Apple Silicon Mac, and we did not try Linux or Windows for either tool.

The escape hatch

Hugo's lock-in is smaller than most. The output is plain HTML you can host anywhere. If you use the file route, the content is Markdown with front matter any other generator can read; if you use the adapter route, it is your own JSON. What does not move is the templates, which are Go templates with Hugo-specific functions. Ours were 29 lines plus a one line partial. Keep them that small and leaving costs an afternoon.

The verdict

Hugo earned its reputation. It was the fastest tool we ran at every size, and its failures, once you know where to look, are all findable. But speed is not the thing to decide on at small sizes, and "findable" means you have to look. If you have Markdown, a growing site, and someone who will pin a version and read the warnings, use Hugo. If you have a small site already in HTML, Eleventy got us to the same place with fewer surprises.

Start with part 1 if you skipped ahead. The empty build that exits 0 is where every one of these lessons starts.

Share

Hugo built 50,000 of our articles in 16.3 s to Eleventy's 24.5 s. It also used nearly twice the memory, and 17 of its last 42 releases flagged a breaking change. #Hugo #Eleventy #StaticSite #WebDev

Never miss a ship

The best stuff that shipped this week, delivered every Thursday. Free, no spam. We read all the boring stuff so you get the fun parts.

Keep reading