Operating Multiple Hugo Versions Side-by-Side

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.

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 found

Hugo’s design makes version management straightforward:

  • Self-contained: Single executable (hugo.exe on Windows, hugo on 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.

  1. Single instance of each Hugo.exe version of the local system.
  2. Pinning Hugo.exe to each git repository, even if all repos use the same version.

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.exe

This is not one-size fits all, and you may consider pinning the desired 50MB+ Hugo.exe to the repo:

  1. 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.
  2. 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)

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.

  • I use a simple PowerShell file - scripts/Start-HugoServe.ps1 which 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.json file 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
    }
  ]
}
  • I put Start-HugoBuild.ps1 in the root to make it easy to run, but if you can remember to press Ctrl+Shift+B to run the default build, maybe the /scripts folder 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 @buildArgs

Again, 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": []
		}
	]
}
StrategyUse WhenKey StrengthsTrade-OffsReproducibility
Repo-pinned binary (tools/hugo-<version>/hugo.exe)Mission‑critical / revenue-impacting / regulatedExact version history; offline; trivial onboarding~50MB per repo; larger clonesFull (clone = build)
Cloud‑synced tools folder (OneDrive/Dropbox/iCloud)Personal / hobby / multi-deviceLightweight repos; central upgrades; easy parallel versionsContributors must set path; CI must download binaryPartial (document path)
Git LFS stored binaryTeam wants pin + smaller default cloneVersion history + smaller initial downloadLFS setup & potential quota limitsFull (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 a VERSION.txt or comment in hugo.toml so you remember which binary belongs to the site.

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.


  • 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.”
{
  "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.