Skip to content

Script to install or update Hugo on macOS

A Bash script that downloads the latest Hugo Extended macOS installer, verifies its SHA-256 checksum, and installs it.

Elvis Finol 3 min read Updated 2026.09.21

This script installs or updates Hugo Extended on macOS from the official GitHub release. It downloads the universal .pkg installer, checks its SHA-256 against the release metadata, and installs Hugo in /usr/local/bin. The universal package covers both Intel and Apple silicon.

As of September 21, 2026, the latest release is v0.166.0. Its macOS packages are named like hugo_extended_0.166.0_darwin-universal.pkg. The old _macOS-64bit.tar.gz pattern is gone, so the script looks up the current release every time you run it.

Before you start

You need an internet connection, an admin account, and curl, jq, and shasum in your terminal. The script checks for those 3 commands before it downloads anything. If jq is missing and you have Homebrew, run brew install jq.

Already managing Hugo with Homebrew? Stay there. brew update && brew upgrade hugo updates it, and brew install hugo handles a first install. Running both methods leaves two Hugo binaries competing in your PATH. The official macOS guide covers that route.

The script installs the Extended edition, which includes LibSass support. It doesn’t include the separate cloud deployment feature. Read the latest release notes before you upgrade an older Mac or an existing site.

Save the script

Copy this into a file named hugo_latest.sh:

#!/bin/bash
set -euo pipefail
fail() {
printf 'Error: %s\n' "$*" >&2
exit 1
}
[[ "$(uname -s)" == "Darwin" ]] || fail "This script requires macOS."
for tool in curl jq shasum; do
command -v "$tool" >/dev/null 2>&1 || fail "Required command not found: $tool"
done
work_dir=$(mktemp -d)
trap 'rm -rf "$work_dir"' EXIT
printf 'Finding the latest Hugo release...\n'
curl --fail --silent --show-error --location \
'https://api.github.com/repos/gohugoio/hugo/releases/latest' \
--output "$work_dir/release.json"
version=$(jq -er '.tag_name | select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))' \
"$work_dir/release.json")
asset_name="hugo_extended_${version#v}_darwin-universal.pkg"
# Require exactly one matching asset with a SHA-256 digest.
asset=$(jq -cer --arg name "$asset_name" '
[.assets[] | select(.name == $name)]
| select(length == 1)
| .[0]
| select(.digest | strings | test("^sha256:[0-9a-f]{64}$"))
' "$work_dir/release.json") || fail "No supported macOS installer with a SHA-256 digest found."
url=$(printf '%s' "$asset" | jq -er '.browser_download_url')
expected_url="https://github.com/gohugoio/hugo/releases/download/$version/$asset_name"
[[ "$url" == "$expected_url" ]] || fail "Unexpected installer URL."
digest=$(printf '%s' "$asset" | jq -er '.digest')
printf 'Downloading Hugo Extended %s...\n' "$version"
curl --fail --silent --show-error --location "$url" \
--output "$work_dir/$asset_name"
printf '%s %s\n' "${digest#sha256:}" "$asset_name" > "$work_dir/checksum.txt"
(cd "$work_dir" && shasum -a 256 -c checksum.txt) \
|| fail "Installer checksum verification failed."
printf 'Installing Hugo (macOS may ask for your administrator password)...\n'
sudo /usr/sbin/installer -pkg "$work_dir/$asset_name" -target /
# Check the installed binary directly, even if PATH selects another Hugo.
installed=$(/usr/local/bin/hugo version)
[[ "$installed" == "hugo $version+extended "* ]] \
|| fail "The installed Hugo did not report the expected Extended version."
printf 'Installed: %s\n' "$installed"
resolved=$(command -v hugo || true)
if [[ "$resolved" != "/usr/local/bin/hugo" ]]; then
printf 'Your shell currently resolves hugo to: %s\n' "${resolved:-not found}"
printf 'Ensure /usr/local/bin is on PATH before any other Hugo installation.\n'
fi

The script stops on any download, checksum, or install error, and it deletes its temporary files on exit.

One caveat about the checksum: it catches a download that differs from GitHub’s release metadata. It’s not an independent signature check.

Run it

Open Terminal, go to the directory where you saved the file, and run:

Terminal window
bash hugo_latest.sh

Or make it executable and run it directly:

Terminal window
chmod +x hugo_latest.sh
./hugo_latest.sh

Run it as your normal user. Only the install step uses sudo, so that’s the single prompt for your admin password. Installing replaces the Hugo binary this package manages in /usr/local/bin.

Verify the installation

Check the installed binary and the one your shell picks:

Terminal window
/usr/local/bin/hugo version
command -v hugo
hugo version

The binary should report the version the script downloaded, with +extended. If command -v hugo points somewhere else, another installation wins in your PATH. Fix that before you assume your site builds with the new version.

Run the script again whenever you want to update. After an upgrade, build your site and read the output against the release notes.