Diagnosing Hugo Build Slowdowns: A Step‑by‑Step Performance Guide
Learn how to spot Hugo build slowdowns, verify the root causes with template metrics and profiling, apply targeted fixes, and know when to escalate for further help.
05 Jan 2026, 15:25 UTC

Recognizable condition
When running hugo or hugo server on a modest workstation (≈4‑core CPU, 8 GB RAM) with a site under 5 000 pages, you observe:
- Initial build time > 30 seconds.
- Incremental reload > 5 seconds (blocks author workflow).
- CPU spikes to 100 % and resident memory grows beyond 2 GB.
Cause / diagnostic table
| # | Typical cause | Why it hurts performance |
|---|---|---|
| 1 | Excessive .Site.RegularPages traversal in layouts (e.g., {{ range where .Site.RegularPages "Section" "blog" }} inside another range) | Results in O(N²) template work as each outer iteration rescans the whole page set. |
| 2 | Uncached resources.Get or images.Filter calls inside loops | Each iteration decodes and resizes the same source image, wasting CPU and memory. |
| 3 | disableKinds not set → Hugo builds taxonomy, term, RSS, sitemap, robotsTXT, 404 pages even if unused | Unnecessary page generation adds I/O and template rendering overhead. |
| 4 | enableGitInfo = true on a large repository | Hugo runs git log for every content file to populate .GitInfo, which can dominate build time. |
| 5 | Missing templateMetrics / templateMetricsHints | No visibility into which partials or templates consume the most time, making optimization guesswork. |
Ordered checks
Run Hugo with template metrics to see the slowest templates:
hugo --templateMetrics --templateMetricsHints 2>&1 | head -30Look for entries with high
durationvalues (e.g., > 500 ms).Generate CPU and memory profiles for deeper inspection:
hugo --profile cpu.pprof mem.pprofThen examine with
go tool pprof -http=:8080 cpu.pprofand look for hot paths intext/template.(*Template).Executeorgithub.com/gohugoio/hugo/resources.Verify that unnecessary kinds are disabled in your site configuration (TOML example):
[params] disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "404"]If you rely on any of those kinds for SEO or UX, keep them enabled.
Check the Git info setting:
enableGitInfo = falseIf you need
Lastmodfrom Git, consider using front‑matter only:frontmatter.lastmod = ["git"]and leaveenableGitInfofalse.Audit layouts for costly page scans. Search for patterns like:
{{ range where .Site.RegularPages }}inside another
rangeorwhere. Replace with pre‑computed slices orfirst/lasthelpers.Inspect image processing calls. Ensure each unique source is processed once and stored in a scratch variable:
{{ $hero := resources.Get "img/hero.jpg" | images.Resize "1200x" }} {{ with $hero }}{{ end }}If the same call appears inside a loop, move it outside.
Fixes tied to findings
Eliminate O(N²) loops: compute a filtered slice once in the page’s
.Scratchor in a partial, then iterate over that slice.{{ $recent := where .Site.RegularPages "Type" "blog" | first 5 }} {{ range $recent }} … {{ end }}Cache image resources: process each source once and reuse the result via a scratch variable or a page‑level resource map.
{{ $src := resources.Get "photos/*.jpg" }} {{ range $src }} {{ $thumb := . | images.Resize "400x" }} … {{ end }}Set
disableKindsto only the kinds you actually render (e.g., keeppageandhome). This reduces the number of pages Hugo must walk through.Turn off
enableGitInfounless you rely on Git‑derived dates. If you needLastmodfrom Git, set it in the front‑matter and keep the flag false.Move expensive image pipelines into a template resource that is executed once:
{{ $opts := dict "src" "img/hero.jpg" "width" 1200 }} {{ $hero := resources.ExecuteAsTemplate "img/hero.jpg" $opts | images.Resize }}Use a deterministic cache key (the concatenated options) so Hugo reuses the processed asset across builds.
Escalation criteria
If after applying the above fixes you still observe:
- Clean build time > 120 seconds.
- Peak memory > 4 GB (risk of OOM on CI agents).
- Incremental reload > 15 seconds (authoring becomes painful).
- A single partial or template accounts for > 5 seconds in
templateMetricswith no obvious optimization. - CI/CD pipelines timeout because the Hugo step exceeds the allotted limit.
At that point, gather the following data and seek help:
- Output of
hugo versionandhugo env. - A minimal repository that reproduces the slowdown (remove themes, content, and assets until the issue persists).
- The generated
cpu.pprofandmem.pproffiles.
Share these in the Hugo Discord #performance channel or open a GitHub issue with the label performance.
Practical verification
Create a reproducible benchmark target in your project’s Makefile:
perf:
@echo "=== Clean build ==="
@time hugo --gc --minify --templateMetrics --templateMetricsHints --profile cpu.pprof mem.pprof
@echo "=== Incremental reload (server) ==="
@time hugo server --disableFastRender --port 1313 --bind 0.0.0.0 &
@sleep 5
@kill $!
Run make perf before a change, record the real time, apply a fix, then run again and compare. Keep the generated profile files for team review; they do not affect the site output.
Limitations and practical checks
- Disabling
sitemap,robotsTXT, or404kinds may break SEO or user experience; verify that your site does not rely on them before turning them off. - Turning off
enableGitInforemoves automaticLastmodfrom Git; ensure your front‑matter supplies a reliable date. - Aggressive
disableKindscan interfere with Hugo’s live‑reload for taxonomies; test withhugo server --disableFastRenderafter changes. - When using
resources.ExecuteAsTemplate, each distinct set of options must produce a unique cache key; otherwise you may serve stale images. - Profiling with
--profileadds roughly 10‑20 % overhead; run it on a clean build, not on an already warm incremental build, to avoid skewed numbers.
To confirm that a fix helped, simply re‑run the perf target and compare the real times; a successful optimization should bring the clean build under ~30 seconds and the incremental reload under ~5 seconds on the reference hardware.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.