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

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 machine | Hugo 0.166.0 | Eleventy 3.1.6 |
|---|---|---|
| Install | 7.86 s, one 89.7 MB binary | 7.77 s, 129 npm packages, 22 MB |
| 128 articles, wall clock | 0.11 to 0.16 s | 0.54 to 0.56 s |
| 10,000 articles, wall clock | 2.43 to 2.48 s | 4.58 to 4.97 s |
| 50,000 articles, wall clock | 16.24 to 16.29 s | 24.51 to 25.50 s |
| 50,000 articles, peak memory | 5.0 to 5.2 GB | 2.7 to 2.8 GB |
| Hugo with part 2's unfixed templates, 50,000 | 86.84 to 92.64 s | (not applicable) |
| HTML code blocks after migration | Intact via adapter, 8 of 128 damaged via Markdown files | Intact, 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
.pkgonly. Scripts that fetched the tarball found nothing to fetch for new versions. - v0.158.0 (March 2026):
.Language.LanguageCodeand 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.allowContenterror 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
| Part | What broke | Exit code |
|---|---|---|
| 1 | Empty project built no HTML | 0 |
| 1 | Draft silently skipped | 0 |
| 1 | Theme demo posts published as ours | 0 |
| 1 | Sitemap and RSS pointing at example.org | 0 |
| 1 | Deleted pages still served from public/ | 0 |
| 1 | go install edition could not compile Sass | 1 |
| 1 | Current theme on Hugo 0.120.0 | 1 |
| 2 | JSON front matter inside YAML fences | 1 |
| 2 | Raw HTML stripped from all 128 articles | 0 |
| 2 | Code blocks rewritten in 8 articles | 0 |
| 2 | HTML content denied by the 0.162 policy | 1 |
| 2 | Every adapter page dated 1 January 0001 | 0 |
| 3 | Quadratic home template: 86 s at 50,000 | 0 |
| 3 | 30 dead internal links, then title-based URLs | 0 |
| 3 | Future date, duplicate slug, empty title and body | 0 |
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.


