Operating Multiple Hugo Versions Side-by-Side
Overview
Has this ever happened to you? You start a new Hugo site for the first time in months and want to use the latest version of the perfect Hugo theme. You wire up your site, and the Hugo build fails because your version of Hugo is too old. So you’re stuck between risking breaking your existing Hugo sites and having to use an older theme to start on your new site. This happened to me very recently. Here is what I learned.
TL;DR: Hugo is a single portable binary—keep separate versioned folders and invoke the exact one you need; no installs, no clashes.
Symptoms
When running hugo serve with an older binary or a mismatched theme, you may see errors like:
ERROR deprecated: .Site.Author was deprecated in Hugo v0.124.0 and subsequently removed. Implement taxonomy 'author' or use .Site.Params.Author instead.
ERROR at <partial "plugin/fontawesome.html" (dict "Style" "solid" "Icon" "adjust")>: error calling partial: partial "plugin/fontawesome.html" not foundhugo.exe Is Self-Contained
Hugo’s design makes version management straightforward:
- Self-contained: Single executable (
hugo.exeon Windows,hugoon macOS/Linux) with no registry writes, no ancillary files. - Portable: Copy anywhere; run from the folder where hugo.toml (or legacy config.toml) is.
- PATH independence: You don’t need Hugo on PATH; invoking via explicit path is fine.
- Invocation precedence: Whichever hugo.exe you call determines the version—it overrides any PATH entry.
This simplicity is a huge advantage. Unlike tools with complex installers or plugin ecosystems, Hugo “just works” from any location, and different versions can be in different locations.
There are two setups that I recommend using depending on requirements and needs.
- Single instance of each Hugo.exe version of the local system.
- Pinning Hugo.exe to each git repository, even if all repos use the same version.
My Solution - Single Version Instances
I added Hugo to a folder dedicated to binaries within OneDrive so that it is backed up and available if I want to develop on a different computer using these steps:
- Log into OneDrive for the Hugo version(s) I need
- Install VS Code if needed
- Clone the Hugo repo(s) from GitHub
Done! Running the appropriate Hugo.exe from the VS Code terminal let’s me work on the site, even if that computer already has an incompatible version of Hugo.exe installed. More on making using the appropriate Hugo.exe easier later.
my-site/
├── hugo.toml
├── scripts/
│ └── Start-HugoServe.ps1
├── Start-HugoBuild.ps1
└── public/
<external-tools>/ # Outside the repo (e.g. OneDrive, shared tools drive)
└── hugo-0.146.0/
└── hugo.exeMy Other Solution - Including Hugo.exe in the Repo
This is not one-size fits all, and you may consider pinning the desired 50MB+ Hugo.exe to the repo:
- When collaborating. For example, I am working with my son on a Hugo site for one of his school’s clubs, and he won’t have direct access to my OneDrive Hugo.exe when he works on the site.
- When making quick updates is crucial. E.g., the Hugo site is a big money maker, an entire department or company depends on the content, etc.
One simple way to implement this is adding a tools/ subfolder to the folder containing hugo.toml, and adding the hugo folder there. This includes the 50MB Hugo.exe in your repository. Note that the first commit may take a little while, but once it’s there, you’re good (until you upgrade to a different Hugo version). For example:
my-site/
├── hugo.toml
├── tools/
│ └── hugo-0.146.0/
│ └── hugo.exe
├── scripts/
│ └── Start-HugoServe.ps1
├── Start-HugoBuild.ps1
└── public/ (generated)Options to Run the Desired Hugo.exe
Now that “Hugo -server” may not run the version you want, here are some strategies to test and build using a single keypress via simple PowerShell and JSON files.
Testing - create 2 files and click F5
- I use a simple PowerShell file -
scripts/Start-HugoServe.ps1which works in VS Code on Windows 10 & Windows 11. Just change the value in$hugoPath
param(
[switch]$Drafts
)
$hugoPath = 'C:\Users\Andy Gettings\OneDrive - BEC\bin\hugo-0.146.0\hugo.exe'
$serveArgs = @('serve', '--disableFastRender', '--renderToMemory')
if ($Drafts) { $serveArgs += '--buildDrafts' }
& $hugoPath @serveArgs- Here is my
.vscode/launch.jsonfile with one config for what production will look like, one for drafts. - Create this file
- Choose the configuration you want in the debug panel
- press F5 to start your site locally.
{
"version": "0.2.0",
"configurations": [
{
"name": "Hugo: Serve site IN MEMORY",
"type": "PowerShell",
"request": "launch",
"script": "${workspaceFolder}/scripts/Start-HugoServe.ps1",
"args": [],
"cwd": "${workspaceFolder}",
"createTemporaryIntegratedConsole": false
},
{
"name": "Hugo: Serve DRAFTS",
"type": "PowerShell",
"request": "launch",
"script": "${workspaceFolder}/scripts/Start-HugoServe.ps1",
"args": [
"-Drafts"
],
"cwd": "${workspaceFolder}",
"createTemporaryIntegratedConsole": false
}
]
}Production Build - two files and Ctrl-Shift-B
- I put
Start-HugoBuild.ps1in the root to make it easy to run, but if you can remember to pressCtrl+Shift+Bto run the default build, maybe the/scriptsfolder is better for you.
Start-HugoBuild.ps1
$hugoPath = 'C:\Users\Andy Gettings\OneDrive - BEC\bin\hugo-0.146.0\hugo.exe'
$buildArgs = @('--gc', '--minify', '--cleanDestinationDir')
& $hugoPath @buildArgsAgain, just set $hugoPath to the correct filename for this site’s Hugo.exe.
Marry that with this .vscode/tasks.json to enable ctrl-shift-B which is the action taken for the task with the "isDefault": true inside the group property. Both strategies are included here:
{
"version": "2.0.0",
"tasks": [
{
"label": "Hugo: Build (Pinned Binary)",
"type": "shell",
"command": ".\\tools\\hugo-0.146.0\\hugo.exe",
"args": [
"--gc",
"--minify",
"--cleanDestinationDir"
],
"group": {
"kind": "build"
}
},
{
"label": "Hugo: Build production (OneDrive)",
"type": "shell",
"command": "pwsh",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"${workspaceFolder}/Start-HugoBuild.ps1"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "Hugo: Serve (OneDrive)",
"type": "shell",
"command": "pwsh",
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-File",
"${workspaceFolder}/scripts/Start-HugoServe.ps1"
],
"isBackground": true,
"problemMatcher": []
}
]
}Strategy Comparison (Quick Reference)
| Strategy | Use When | Key Strengths | Trade-Offs | Reproducibility |
|---|---|---|---|---|
Repo-pinned binary (tools/hugo-<version>/hugo.exe) | Mission‑critical / revenue-impacting / regulated | Exact version history; offline; trivial onboarding | ~50MB per repo; larger clones | Full (clone = build) |
| Cloud‑synced tools folder (OneDrive/Dropbox/iCloud) | Personal / hobby / multi-device | Lightweight repos; central upgrades; easy parallel versions | Contributors must set path; CI must download binary | Partial (document path) |
| Git LFS stored binary | Team wants pin + smaller default clone | Version history + smaller initial download | LFS setup & potential quota limits | Full (after LFS pull) |
Git LFS internals and ad hoc / ignored local binary approaches are beyond this post—they add friction without improving reproducibility.
Hobbyist note: Even without Git or CI you can still name folders clearly (e.g.
C:\HugoVersions\hugo-0.146.0\) and drop aVERSION.txtor comment inhugo.tomlso you remember which binary belongs to the site.
Summary
Operating multiple Hugo versions side-by-side is straightforward once you understand Hugo’s self-contained nature. Whether you commit binaries for reproducibility or reference external paths for convenience, the key is intentionality: document your strategy so collaborators and future-you understand the trade-offs.
To keep your sanity add documentation in a README or elsewhere about what version your Hugo site uses and where this repo expects to find it. Your mileage may vary—choose the pattern that matches your project’s needs.
Points Discussed
- Goal: Run different Hugo versions per project without manual reinstalls.
- Simplest path: Create
tools/hugo-<version>/in each repo; call the binary by explicit path. - Dive deeper: AI says, “See JSON capsule below for structured data.” Me, “lol.”
Only a Machine Reader Would Care:
{
"context": {
"hugo_version": "0.146.0",
"platform": "Windows",
"build_type": "extended"
},
"errors": [
{"match": ".Site.Author was deprecated", "action": "Use .Site.Params.Author or implement taxonomy 'author'"},
{"match": "partial \"plugin/fontawesome.html\" not found", "action": "Update theme / ensure partial exists; upgrade to compatible Hugo version"}
],
"strategies": [
{
"id": "repo-pinned",
"path_example": "tools/hugo-0.146.0/hugo.exe",
"when": "mission-critical / revenue-impacting / regulated",
"pros": ["exact reproducibility", "works offline"],
"cons": ["~50MB per repo"]
},
{
"id": "cloud-synced",
"path_example": "C:/Users/Name/OneDrive/bin/hugo-0.146.0/hugo.exe",
"when": "personal / hobby / multi-device",
"pros": ["lightweight repo", "central upgrades"],
"cons": ["manual path setup for collaborators"]
},
{
"id": "git-lfs",
"path_example": "tools/hugo-0.146.0/hugo.exe (LFS tracked)",
"when": "team wants pinning + smaller default clone",
"pros": ["version history", "smaller initial clone"],
"cons": ["LFS setup / quota"]
}
],
"dev_server": {
"command": "hugo serve --renderToMemory",
"notes": ["avoids overwriting existing public/", "add --buildDrafts when drafting"]
},
"prod_build": {
"command": "hugo --gc --minify --cleanDestinationDir",
"notes": ["garbage collect caches", "minify assets", "remove stale outputs"]
},
"pin_decision": {
"questions": ["Is breakage expensive?", "Do you need reproducible diffs?", "Regulatory review required?"],
"result": "If >=1 yes → repo-pinned or git-lfs; else cloud-synced"
},
"upgrade_steps": [
"Download extended binary",
"Add new version folder (tools or cloud bin)",
"Update script paths",
"Run dev + prod builds",
"Diff output (optional)",
"Update docs"
]
}Parser Note: The JSON capsule above is machine-extractable. Use it for automation, CI setup, or Copilot context enrichment.