Manage Hugo Module Dependencies Without Losing Track of Them
hugo mod turns a Hugo site into a proper Go module, and this guide walks the whole lifecycle end to end. You will initialise the module, add a dependency, inspect the graph, tidy the metadata, verify what got downloaded and optionally vendor it locally. Allow ten to twenty minutes for a small site, plus download time for anything you add.
The route
Jump straight to the step you need, or tick off Done means at the end.
The examples target the installed Hugo 0.123.7, built on 17 March 2026. Run everything from the directory holding your site's hugo.yaml, hugo.toml or hugo.json. These commands change project files or the module cache, so commit or copy your work before touching dependency versions.
Before you start
Check your tools and project directory first:
$ hugo version
hugo v0.123.7+extended linux/amd64 BuildDate=2026-03-17T19:51:14Z VendorInfo=ubuntu:0.123.7-1ubuntu0.3+esm2
$ command -v go
/usr/bin/go
$ command -v git
/usr/bin/git
$ pwd
/home/you/src/my-site
Your paths and build metadata will differ. The parent hugo mod command says most operations need Go 1.12 or newer plus a version-control client, usually Git. Skip those tools entirely for a site that only uses modules inside themes, or one that already vendors its modules.
Checkpoint
Do not continue until hugo version identifies the binary you actually mean to use, and you are sitting in the right project directory.
1. Initialise the project as a module
Initialisation creates the Go module metadata Hugo leans on for dependency resolution. Give it the real module path when the directory name does not make that obvious:
$ hugo mod init example.com/acme/my-site
go: creating new go.mod: module example.com/acme/my-site
The path is an identifier, not a download instruction. Use wherever this project is published or consumed, such as your Git hosting path. Hugo will guess it if you omit the argument, but an explicit value avoids a rename later.
Check the result before doing anything else:
$ sed -n '1,40p' go.mod
module example.com/acme/my-site
go 1.XX
The Go directive comes from the installed toolchain and can vary. If go.mod already exists, stop and read it rather than running initialisation on autopilot: this command establishes a module, it does not repair an existing one.
2. Add one dependency at a time
Use hugo mod get to resolve a module. This example leans on Hugo's own documented test module as a stand-in; swap it for whatever your site actually needs:
$ hugo mod get github.com/gohugoio/testshortcodes
go: added github.com/gohugoio/testshortcodes v0.3.0
Exact version and messages depend on what the module has released. Append a version when you want a particular one:
$ hugo mod get github.com/gohugoio/[email protected]
For repeatable work, pin a reviewed version or tag rather than pulling an unbounded update. hugo mod get updates the module files and downloads source into Hugo's cache; it does not make anything visible in the site's output until your configuration mounts or uses those components.
Update direct dependencies on purpose. This asks for the latest possible versions of direct dependencies, while ./... also processes module paths recursively:
$ hugo mod get -u
$ hugo mod get -u ./...
Warning
Version updates can change layouts, partials, assets or configuration a module supplies. Review the diff and build the site before committing anything. If an update goes wrong, restore the reviewed go.mod and go.sum from version control and build again; do not start deleting files by hand to try to undo it.
Checkpoint
Record the change:
$ git diff -- go.mod go.sum
$ git status --short
3. Inspect the dependency graph
Run the graph command from the module directory:
$ hugo mod graph
example.com/acme/my-site github.com/gohugoio/[email protected]
Each line names a parent and a dependency. The real graph can include indirect modules, replacement information, disabled modules and an in-themesdir entry. For a vendored dependency, the graph reports whatever version sits in the vendor tree, which may not match what is written in go.mod.
If the graph comes back wrong, check you ran it from the intended module tree and that the project's module path matches the files you inspected. Reach for the inherited --config, --configDir, --themesDir and --source options only when your site deliberately keeps those locations elsewhere.
4. Tidy and verify the downloaded source
Once the site builds with the components you intend, clear the unused entries from the Go metadata:
$ hugo mod tidy
$ git diff -- go.mod go.sum
Tidy can remove entries that looked useful but the current module graph no longer needs. Review its diff; it is a project-file change, so it needs no root privileges.
Then check the cached dependencies have not changed since download:
$ hugo mod verify
all modules verified
Successful output varies, but the command should exit with status 0. A verification failure means the local cached source no longer matches its recorded checksums. Investigate the cache and the machine before reaching for the cleanup option: hugo mod verify --clean deletes the module cache entries that fail verification. That is reversible by downloading them again, though it needs network access and is no substitute for understanding why they changed.
5. Vendor when you need a repository-local copy
Vendoring copies imported module dependencies into _vendor inside the project. Hugo prefers that directory when resolving components, so it gives builds a local copy you can inspect and commit:
$ hugo mod vendor
$ find _vendor -maxdepth 2 -type f | head
$ git status --short
Do not edit files inside _vendor as your first move. To override a vendored file, create the matching path in the project root, the way Hugo documents. Modules below themes are never vendored.
Warning
Vendoring can add a large tree to the repository. Check its size, licence obligations and review policy before committing it. To undo a vendor operation, remove the generated _vendor directory only once you have confirmed it holds none of your own work, then restore the working tree or regenerate it from the reviewed module files. The command itself needs no sudo.
When local edits need to be seen instead of the vendored copy, the parent command's --ignoreVendorPaths option can ignore matching vendor paths. Use a narrow glob for a known module, not a blanket setting you have not actually tested.
6. Clean the cache only with a reason
hugo mod clean deletes the Hugo Module cache for the current project. Handy when a cache is damaged or you need a forced fresh download, but it can make the next build slower and dependent on the network:
$ hugo mod clean
$ hugo mod verify
The --pattern option narrows the cleanup to matching module paths. --all cleans the entire module cache, not just this project, so treat it as a maintenance action. Neither needs elevated privileges when the cache belongs to your user; skip sudo, since changing the cache's ownership just creates a second, harder-to-diagnose failure.
7. Run the final build
Resolve the site once more and build it to a disposable destination before committing any dependency change:
$ rm -rf /tmp/hugo-module-preview
$ hugo --destination /tmp/hugo-module-preview
Start building sites ...
| EN
---------------------+----
Pages | 1
Total | 1
Page counts and timing depend on the site. The disposable destination is safe to remove once you have looked it over. If the build fails, read the first module or mount error, compare hugo mod graph against your configuration, and restore the last known-good dependency files before trying again.
Done means
hugo versionshowed the installed Hugo 0.123.7 binary, or you recorded why another binary was selected.- The module path in
go.modmatches the project you meant to change. - Every added or updated dependency is visible in a reviewed diff.
hugo mod graphshows the dependency and its resolution path.hugo mod tidyleft only the entries the site still needs.hugo mod verifycompleted cleanly, or its failure is understood.- Any
_vendortree is intentional, reviewed and recoverable from version control. - A final Hugo build succeeds without root privileges.