Tutorial1 day ago

1. Installing Hugo, and the Empty Build That Exits 0

Hugo installed in 7.86 seconds and rebuilt our site in 4 ms. Its first build also wrote zero HTML files and reported success, and that was the first of four silent failures.

The WJS Desk

Sep 23, 2026 · 10 min read

Photo by Wendelin Jacober on Pexels

By the end of this part you will have Hugo installed, a site that actually renders a page, a live preview that rebuilds in single-digit milliseconds, and a clear picture of the four ways a first Hugo build succeeds while producing something you did not want. That last part is the reason this course exists. Hugo's first build on a fresh project exits with status 0 and writes zero HTML files, and it only tells you in two warning lines.

Here is what we measured on an Apple M4 Pro running macOS 26.5.1 (build 25F80), Homebrew 7.0.6, on a home connection. Every timing comes from /usr/bin/time -p or from Hugo's own build summary, and we say which.

StepTimeNotes
brew install hugo7.86 s0.166.0 bottle, 89.9 MB on disk, zero dependencies
go install (the other route)282.96 sAlso pulled a Go 1.27.1 toolchain, 1.78 GB of cache
hugo new site0.02 sTwo files
First build, empty project0.03 sExit 0, no HTML written
Rebuild after an edit, under hugo server4 to 6 msHugo's own "Total in" figure, three edits

What Hugo is, and why it gets a course

Hugo is a static site generator written in Go. You give it a folder of Markdown files and a folder of templates, it gives you a folder of HTML you can put on any web host. No database, no server process in production, no Node toolchain unless you choose to add one.

We picked it for a four part course because it passes every test we set for one. The repo sat at 89,910 stars when we checked, with a push the same morning and a v0.166.0 release on 9 September 2026. It is Apache-2.0. And it is not one person's hobby: in the six months to 23 September we counted 368 commits, 222 from Bjorn Erik Pedersen (bep, the lead), 44 from Joe Mooring (jmooring), 77 from Dependabot and the rest from a long tail of contributors. That is concentrated, but it is two humans who both ship, which is more than several tools with bigger marketing can claim.

We also looked at Pandoc (46,385 stars, but 516 of its last 565 commits are from its author), Slidev, Zola and mdBook. Pandoc is brilliant and a single point of failure. Hugo is the one people will still be searching for next year, partly because it has been around since 2013 and partly because it keeps changing under its users, which is exactly what makes a hands-on guide worth having.

Prerequisites and how long this takes

  • macOS with Homebrew. We did not test Linux or Windows in this part.
  • Git, only if you want a third-party theme. Xcode's command line tools include it.
  • About 15 minutes if you follow along, most of it reading. The commands themselves take under 10 seconds combined.

You do not need Go, Node or Sass for anything in this part. That surprises people coming from JavaScript site builders, and it is Hugo's single biggest selling point.

Step 1: install it, and pick the right route

brew install hugo
hugo version

Ours printed:

hugo v0.166.0+extended+withdeploy darwin/arm64 BuildDate=2026-09-09T14:45:02Z VendorInfo=Homebrew

Read that version string, because it tells you which of Hugo's four editions you have. extended means it can compile Sass with the bundled LibSass and encode WebP images. withdeploy means it includes the hugo deploy command for pushing to S3, Google Cloud Storage and Azure. Homebrew gives you both, which is the edition you want unless you have a reason not to.

Pro tip: if an older tutorial tells you to download a file like hugo_extended_0.152.0_darwin-universal.tar.gz from the releases page, the equivalent file does not exist for current versions. We checked the release assets: v0.152.0 (21 October 2025) was the last to ship macOS tarballs, and from v0.153.0 (19 December 2025) onward macOS only gets a .pkg installer. Scripts that curl the tarball into a CI runner break on any version from 0.153 up. Use Homebrew locally.

Step 2: create a project, and watch the build lie to you

mkdir -p ~/hugo-course
cd ~/hugo-course
hugo new site mysite
cd mysite
hugo

hugo new site took 0.02 seconds and created exactly two files: hugo.toml and archetypes/default.md. Then hugo built the project in 8 ms by its own count and printed this:

WARN  found no layout file for "html" for kind "home": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.
WARN  found no layout file for "html" for kind "taxonomy": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.

                  │ EN
──────────────────┼────
 Pages            │  4

Exit status 0. Four "pages". And the public/ folder contained four files, none of them HTML: index.xml, sitemap.xml, and RSS feeds for the empty tags and categories taxonomies. There is no index.html. If you ran this in CI with only an exit code check, you would deploy an empty site with a sitemap.

This is not a bug. Hugo ships with no templates on purpose, the idea being that the look belongs to a theme or to you. But it is the single most common reason people give up in the first ten minutes, and the message is two lines of warning in a wall of output.

Step 3: give it templates

You have two choices: generate a skeleton theme, or clone someone else's. Start with the skeleton, because it shows you what a theme is made of.

hugo new theme scratch
echo 'theme = "scratch"' >> hugo.toml
hugo new content posts/first-post.md
echo "Hello from the body." >> content/posts/first-post.md
hugo

hugo new theme wrote 26 files in 0.02 seconds: a baseof.html wrapper, one template each for home, page, section, taxonomy and term, seven partials, some CSS and JS, and a content/ folder with three sample posts. The build now reported 18 pages in 12 ms and wrote a real public/index.html.

Open public/posts/ and you will find post-1, post-2 and post-3. You will not find first-post, which is the one you just wrote.

What broke: four silent failures in the first ten minutes

Your first post is a draft, and drafts are skipped

Here is the file hugo new content wrote for us:

+++
date = '2026-09-23T21:02:09+08:00'
draft = true
title = 'First Post'
+++

draft = true is the default in the archetype, and a normal hugo build excludes drafts without saying so. We got 18 pages without the flag and 19 with it:

hugo -D

There is no warning and no count of skipped drafts. The fix for real content is to delete the draft = true line, or change it to false, once the post is ready. Use -D only for previewing.

The theme's demo posts are now in your site

Those three post-N pages came from themes/scratch/content/. Hugo merges a theme's content folder into yours, so the skeleton's sample posts, including a photo of Bryce Canyon, ship with your site until you delete them. On a real site you would publish three placeholder articles and never notice, because they look like your own.

rm -rf themes/scratch/content
hugo

The build dropped from 18 pages to 9. Then we listed public/posts/, and all three demo posts were still there, along with our draft from the -D build. Hugo writes into public/ but does not delete from it, so anything you remove from the source keeps being served until you clean the folder yourself. The files had the timestamp of the earlier build, which is the only clue.

hugo --cleanDestinationDir

That flag, or a plain rm -rf public before building, left public/posts/ holding only its own index.html and index.xml, and the whole output shrank to 12 files. Make it a habit before any deploy.

Everything points at example.org

The default hugo.toml sets baseURL = 'https://example.org/'. After our first themed build, 8 of the 27 files in public/ contained that domain, including every <loc> in the sitemap and every link in the RSS feed. Pages themselves use relative links, so the site looks right in a browser and only the machine-read files are wrong. That is the worst combination, because you find out from Search Console weeks later.

sed -i '' "s|https://example.org/|https://your-real-domain.com/|" hugo.toml

Watch out: none of these problems changes the exit code. An empty site, a site missing your drafts, a site full of someone else's sample posts, and a site still serving pages you deleted all build with status 0. If you script Hugo, check for public/index.html and grep the sitemap for your own domain.

Step 4: the live preview loop

hugo server -D

The server bound to 127.0.0.1:1313, did its first build in 13 ms, and served the page with our draft included. We then appended a line to the post three times, 1.5 seconds apart. Hugo's log reported rebuilds of 6 ms, 5 ms and 4 ms, and a fresh curl returned the third edit. On a two-post site, the browser refresh is slower than the rebuild. We will see how that holds up on a real site in part 2.

Note that the server binds to localhost by default. If you need to check the site from a phone on the same network, hugo server --bind 0.0.0.0 exposes it, which is fine for a blog and not fine for anything private.

The obvious wrong turns we tried on purpose

Installing with go install

If you already have Go, go install github.com/gohugoio/hugo@v0.166.0 looks like the natural route. We had Go 1.25.7. Hugo 0.166.0's go.mod asks for Go 1.27.0, so Go quietly downloaded a 1.27.1 toolchain and built with that. The whole thing took 282.96 seconds, left 881 MB in the module cache and 896 MB in the build cache, and produced a binary reporting hugo v0.166.0 darwin/arm64. Notice what is missing: no +extended. That leads straight to the next failure.

The standard edition and Sass

We wrote a one-line Sass file and a template that calls css.Sass on it. The Homebrew build compiled it in 24 ms. The go install build failed with:

TOCSS: failed to transform "/css/main.scss" (text/x-scss). Check your Hugo installation; you need the extended version to build SCSS/SASS with transpiler set to 'libsass'.

To its credit, that error tells you exactly what is wrong. Many themes use Sass, so the standard edition fails on them the moment you switch. Building extended from source needs CGO and a C compiler, and we did not try it, because Homebrew already solves the problem.

An old Hugo with a current theme

We cloned PaperMod, the most-starred Hugo theme at 13,937 stars, and built it with the current Hugo in 22 ms. It worked, with two deprecation warnings about .Language.LanguageDirection and .Language.LanguageCode, both deprecated in v0.158.0. Then we downloaded Hugo 0.120.0 (from October 2023, the sort of version an old CI image or a slow-moving package repository hands you) and ran the same site. It printed a compatibility warning, then a stack of "found no layout file" warnings, then:

ERROR => hugo v0.146.0 or greater is required for hugo-PaperMod to build

PaperMod's theme.toml declares min_version = "0.146.0". Version 0.146 is when Hugo reorganised its template folder layout, moving partials to layouts/_partials among other things, and older binaries cannot find templates in the new places. If a theme builds for its demo site and not for you, run hugo version before anything else.

Common mistakes

  • Treating exit status 0 as success. On a project with no templates, it means "nothing to do".
  • Leaving draft = true on a finished post and wondering where it went.
  • Shipping a theme's content/ folder along with the theme.
  • Deleting a page from the source and assuming it is gone from public/.
  • Forgetting baseURL, so the sitemap and RSS feed point at example.org.
  • Using go install and getting the standard edition, then hitting a Sass theme.
  • Following a tarball download from an older tutorial and getting a 404 for current versions.
  • Pairing a theme with a Hugo older than its min_version.

What we did not test

We ran everything on one machine: Apple Silicon, macOS 26.5.1, Hugo 0.166.0 from Homebrew, Go 1.25.7 for the source build. We did not test the .pkg installer, Linux distribution packages, Windows, Docker images, or Snap. The 7.86 second install time is a cold download on our connection and will vary with yours. The single-digit rebuild times are on a site with a handful of pages, which proves nothing about large sites. That is part 2's job.

Rolling back

Hugo keeps its binary, your project folder, and a cache at ~/Library/Caches/hugo_cache (ours was still empty after this part). Undoing all of it is three commands:

brew uninstall hugo
rm -rf ~/Library/Caches/hugo_cache
rm -rf ~/hugo-course

If you tried the go install route as well, the cache is the part worth reclaiming:

go clean -cache -modcache

Next

You now have a Hugo that works and a healthy suspicion of its exit code. In part 2 we stop using toy posts and build a real site from 100 real articles, with our own templates, tags, and the timings to show what Hugo's speed claim looks like at a size that matters.

Share

Hugo's first build on a fresh project exits 0 and writes zero HTML files. We found four ways it reports success while shipping the wrong site. #Hugo #StaticSite #OpenSource #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