Build a Hugo Site Safely from the Command Line

Point Hugo at the wrong destination, or run its clean flag without checking first, and it will delete files that were never Hugo's to remove. You will finish with a repeatable command-line build for a Hugo site, a separate preview output directory, and checks for content that Hugo normally leaves out. The examples use the installed Hugo v0.123.7, packaged on this machine as 0.123.7-1ubuntu0.3+esm2.

Allow about fifteen minutes. You need a shell, a Hugo project with its configuration file, readable content and templates, and a destination directory you control. These examples do not require root.

Warning: do not run the build as root merely because the final web server directory is protected. Build somewhere writable, then use the deployment process that owns that directory.

1. Confirm the installed command

Check the binary and version before relying on a flag. This is read-only:

$ command -v hugo
/usr/bin/hugo
$ hugo version
hugo v0.123.7+extended linux/amd64 BuildDate=2026-03-17T19:51:14Z VendorInfo=ubuntu:0.123.7-1ubuntu0.3+esm2

The manpage describes hugo as the main command for building a site, alongside subcommands such as hugo server and hugo version. Keep this guide's build command separate from the development server: a server is useful for previewing, but a build gives you files you can inspect and deploy.

Checkpoint: if command -v hugo finds nothing, stop and install or enable Hugo through your normal package-management process. Do not copy a binary from an untrusted location.

2. Set the project and destination explicitly

Change these two values to your own paths. The project path should contain hugo.yaml, hugo.json or hugo.toml, unless you pass a different configuration arrangement:

$ SITE='/srv/example-site'
$ OUT='/tmp/example-site-public'
$ test -f "$SITE/hugo.yaml" || test -f "$SITE/hugo.json" || test -f "$SITE/hugo.toml"
$ mkdir -p "$OUT"

The --source flag tells Hugo which project to read, while --destination selects where generated files go. Using both makes it harder to build the wrong checkout or mistake a source directory for generated output. If your project uses a non-standard configuration file or directory, inspect its existing build instructions before adding --config or --configDir.

Checkpoint: print the values before building:

$ printf 'source: %s\ndestination: %s\n' "$SITE" "$OUT"
source: /srv/example-site
destination: /tmp/example-site-public

3. Run a normal build

Build the site into the explicit destination:

$ hugo --source "$SITE" --destination "$OUT"
Start building sites ...
                  | EN
-------------------+-----
  Pages            |  12
  Paginator pages  |   0
  Non-page files   |   4
  Static files     |   4
  Processed images |   0
  Aliases          |   2
  Sitemaps         |   1
  Cleaned          |   0

                   | EN
-------------------+-----
  Total            |  12

Built in 42 ms

The counts depend on the project, and timing is variable. The result that matters is a successful exit status and files in $OUT. Check both without changing anything:

$ printf 'exit status: %s\n' "$?"
exit status: 0
$ find "$OUT" -maxdepth 2 -type f -print | sort | head
/tmp/example-site-public/index.html
/tmp/example-site-public/index.xml

If Hugo reports a template, front matter or content error, fix that error in the project and rerun the build. A partial destination is not evidence of a valid publication.

4. Understand content that is excluded by default

Hugo normally excludes content marked as draft, content whose publication date is in the future, and content whose expiry date has passed, using front matter fields commonly named draft, publishDate and expiryDate. This is a useful safety default: an unfinished page does not go public just because someone ran a build.

List the categories of excluded content before deciding whether to include them:

$ hugo list drafts --source "$SITE"
$ hugo list future --source "$SITE"
$ hugo list expired --source "$SITE"

To make a deliberate staging build that includes all three categories, add the matching flags:

$ hugo --source "$SITE" --destination "$OUT" \
    --buildDrafts --buildFuture --buildExpired

Do not carry those flags into a production build unless your publication process explicitly requires them. If the output unexpectedly contains a draft or future page, inspect the command, configuration and front matter first: do not use the flags to hide a date or metadata mistake.

5. Preview without changing the published tree

Keep preview output separate from the directory served by the web server. For a local preview, use Hugo's server command from the project directory:

$ cd "$SITE"
$ hugo server --buildDrafts
Web Server is available at http://localhost:1313/

The exact log includes the site configuration and can vary. Open the local address only when you trust the content and know who can reach the listening interface. Stop the server with Ctrl-C, the undo operation for this step.

Warning: a server preview does not publish files, but it can expose local content if you configure it to listen beyond localhost.

6. Treat cleanup and cache options as separate decisions

Destructive action: --cleanDestinationDir removes files from the destination that Hugo does not find in the project's static directories. That can delete an old page, a manually copied verification file or an artefact from another tool. Leave the flag out until you have confirmed the destination is dedicated to Hugo output and can be rebuilt.

If you do need a clean dedicated output directory, make a backup or use a new directory first, and compare the result before replacing the served tree:

$ CLEAN_OUT='/tmp/example-site-clean-public'
$ mkdir -p "$CLEAN_OUT"
$ hugo --source "$SITE" --destination "$CLEAN_OUT" --cleanDestinationDir
$ find "$CLEAN_OUT" -type f -print | sort | head

Hugo's --gc option removes unused cache files after the build; it is not a replacement for checking the output, and it can make a later build do more work. --ignoreCache ignores the cache when you are investigating stale generated resources. Use those options to diagnose a specific problem, not as ritual switches.

7. Check the hand-off

Before a deployment step, inspect the output path and compare it with the site you intended to build:

$ test -s "$OUT/index.html"
$ find "$OUT" -type f | wc -l
$ git -C "$SITE" status --short

The first command checks that an index file exists and is non-empty, if your site has a home page. The file count is a rough change signal, not a correctness proof. The Git status command is read-only and helps catch source changes you forgot to review.

Warning: do not deploy solely because Hugo returned zero. Read the rendered page and check links, or run the project's own tests as well.

If a build wrote to the wrong destination, nothing needs undoing in the source tree. Remove or quarantine that generated directory using your normal file-retention process, then rerun with an explicit destination. Do not delete a shared web root to repair a path mistake.

Done means