Hugo modules: a collection of questions

I am finally getting an understanding of Hugo modules. The following is a loosely collection of questions that came up while trying to get the full picture from the documentation (I am trying to rewrite the documentation to be more friendly to non-Go-developers in the end):

  • A folder declared in the config option private is
    a) downloaded when adding the module but not available to Hugo or
    b) not downloaded when Hugo loads the module?
  • Is the config.toml of a module merged into the local configuration or the other way around?
  • What happens if the module config.toml declares the following section:
    home = [ "HTML", "RSS", "Algolia", "REDIR", "HEADERS" ]
    Does this override the local config, is merged (adding the items in home into the local home slices or is it ignored if a local config option for home exists? In my specific case I created a module that adds Netlify headers as a content type to a website. When adding these lines above to the root-config, Hugo does not know YET the redir and headers output format. See this issue.
  • “When you add a mount, the default mount for the concerned target root is ignored: be sure to explicitly add it.” - what does that mean?
  • Based on my experiments I assume that a module, whose mount configuration does NOT have an explicit target set will be mounted into themes. Is that right?

  • Some posts in the Hugo-Discourse say, that the title in hugo mod init title when you make your repository/theme/component a module is more or less arbitrary. In my experience if the module lives in Github under davidsneighbour/some-repo-name and I init it as blafasel then it leads to notices when updating the module, that the module declares itself as but is mounted as blafasel (or the other way around). This seems to be just a notice, but will this discrepancy lead to issues further down the road? Wouldn’t it be better to init all modules with a name that has the URL to the repo without the protocoll in the beginning?
  • Testing local changes > replace => /Users/bep/hugotestmods/mypartials - this somehow breaks my live site. I assume this is a change that I should do only locally and not commit to the repository. Is there any way to permanently add this change to my setup without breaking all implementing sites?
  • Vendoring: If I have a root module, that adds a theme module that itself uses another module and run hugo mod vendor in the root module, will this vendor ALL modules or only the next level?

And lastly a tip:

If you have a module, that is a private repository on Github, you will experience issues when you add the repo with an URL as path to the module. Hugo can’t download the module in that case. You can fix that by adding the following lines to your ~/.gitconfig file:

[url "ssh://"]
  insteadOf =

This will force git to access the repository via SSH link even if it is set up via HTTPS. Make sure to have set up authentication via SSH key.

That’s all for now. Sorry for the amount of questions, mods feel free to send this post to annihilation if it’s unfitting.


@davidsneighbour Thanks for the effort you’re putting in to make Hugo Modules easier for us non-Go-dev types to understand — truly a worthy endeavor. :+1:

What is private?

For config merging from modules, we currently merge

  • params, languages, menus, output formats, media types
  • we merge from left to right (as in: we keep the value on the left is key is defined in more than one module)
  • we only merge maps
  • we do not merge slices

could that somehow be something you can implement? I think in the case of the outputs > home config it could help to add “custom post types” somehow.

We have had that discussion before, and there is no way that can be done without additional information.

Take this from my project:

# I want HTML only, pretty please!
home = [ "HTML" ]

A theme module defines:

home = [ "HTML", "RSS", "Algolia", "REDIR", "HEADERS" ]

Hugo cannot know if the site owner needs/wants the additional output formats. And the example above isn’t even the hardest case one could imagine.

How about:


two_colors = ["blue", "red"]


two_colors = ["green", "red"]

Should this be:

two_colors = ["blue", "green", "red"]


Yes. Why not? If I add a module that adds “green” to the “two_colors” param why would I not want this? I think that if anybody is adding a module/submodule/component they know that they add configuration to their setup.

I think that adding a module means that the person adding this knows that they are working deep in the inner parts of their setup. It’s not like they are by accident add some lines of code?

These are great questions! I’ll be interested to read everyone else’s experiences.

I’ll just respond to the one where I definitely know the answer.

I assume you know that the bep in the path is unlikely to work on your machine, unless you are bep, of course. :joy:

More seriously (and assuming those were just example paths you gave), if you build your site locally and push your public/out dir up to your publishing location, there’s no reason for it to break your live site. If, however, you’re building server-side (on your CI, for example), then the CI won’t have the local replacement dir available to it.

Depending on where you build/compile, there’s no reason not to commit your replace directive, other than that will create a burden on anyone else using the repo to have the same local path. And then you might as well make it require rather than replace.

As for having go.mod have a “local” vs a “server” flavour, I don’t think go supports this. But you can generate the replace directive from the cmd line, so you could make yourself a helper script that’ll configure it for you easily enough.

I keep a local copy of go.mod for client-side builds of Hugo where I generally work with in-progress or not-released-yet modules and it works quite well. I don’t commit it, though.

1 Like

That’s what I mean. We need a local version of go.mod and one that is in the module release commited to the repository without the replace parameters. I think an easy way to accomplish this is to add go.mod to .gitignore and manually adding changes (without the replace lines) to the repo.

You will never know if I am @bep or not :wink:


there is a cmd-line option to generate the replace => directive for you - it doesn’t really do anything more fancy than just add the line to the .mod file, but it’s a quick way of generating your test setup.


# Add a replace directive.
$ go mod edit -replace

# Remove a replace directive.
$ go mod edit -dropreplace
1 Like

Because I have two boxes to make pretty. Not 3. So any override of slices needs to be of the complete value.

PHP has array_merge, which is basically taking arrays (slices and maps) and, well, merging them… I don’t understand the problem Golang has here, mostly because I don’t come from a Go point of view… I understand that it’s not possible, but somehow I think that’s a feature Golang would want to have.

If the root config has

something = ['bla', 'fasel']

and the modules config has

something = ['something', 'else']

why is creating the following combined configuration impossible?

something = ['bla', 'fasel', 'something', 'else'] 

I think that’s a “feature request” :wink:

The problem here isn’t a technical one. To merge two slices of 4 items so it becomes 8 items (what order?) may be what you want in some situations, but I would say it’s rare, and it would remove option to get the most commonly wanted behaviour: The theme defines the palette blue and green, I want the palette red and yellow.


My question is: What are modules and why would I need them. Furthermore, if I need them, do I need to use Github? Can’t I do without?

This has not become clear reading the documentation.

Hugo modules are Go modules. They are used to manage dependencies, mount other repositories (for example a remote contentDir) etc.

Hugo Modules require an installation of Go for the project to be built and deployed.

No. You do not have to use GitHub, any Git remote can be mounted as a Hugo Module.

Underneath it all Go Modules are identical to Git Submodules.

Using them is a matter of preference. For example I do not use Hugo Modules in my projects because I do not want the Go dependency.

For others who manage complex projects, Hugo Modules make their lives easier.


Hi, thanks very much for your reply. I now understand that Hugo Modules are Go modules and I could use them to manage dependencies. So for my small sites I don’t think I need it.
Can I still use Themes? When I try, (running hugo server -D in a project with cloned ananke theme) the message “Error: module “gohugo-theme-ananke” not found; either add it as a Hugo Module or store it in “/Users/Frank”.: module does not exist” appears.
I thought I used the old fashioned way to add the theme.

The standard way of using themes still applies. Nothing has changed in that department except the lingo.

In Hugo land everything is now considered a module (hence the console message).

It appears that Hugo cannot find the theme in your project. Simply clone the ananke theme, place it under /themes/ and then call it from your project’s config.

If you are still unable to solve the issue then please open another support topic and share a repo that reproduces the issue.

1 Like