Installing Hugo on Linux: The Reliable Way

Written by oxypteros

Recently, a user trying to get started with my theme, got stuck. They had tried to install Hugo on Ubuntu, followed a few different guides, and ended up with multiple, conflicting Hugo versions on their system. Their frustration was palpable, and it’s a story I’ve come across more than once.

The official Hugo docs are great, but the Linux ecosystem has a few quirks. This guide is my attempt to provide a crystal-clear, “do this and it will work” method for Linux users.

Three Rules

Before we touch the terminal, let’s establish three rules. Following these will save you from 99% of future problems.

  1. Avoid Your Distro’s Package Manager. This is the most important rule (apt, dnf, etc) almost always contain outdated versions of Hugo. In some cases two years old.
  2. Check Your Theme’s Requirements. Themes require a minimum Hugo version to work. Always check the theme’s theme.toml file first.
  3. Update How You Installed. If you install manually, you must update manually. If you used a snap package, stick with that. Mixing installation methods is the fastest way to break your setup.

With that said, let’s install Hugo.

My recommendation: Manual Installation

The safest, most reliable, problem-free method is to install the pre-compiled binary from Hugo’s official GitHub releases. It has never failed me.
This is my preferred method for all my Linux machines. It’s clean, simple, and gives you full control.

Step 1: Download the Correct Archive

Go to the Hugo Releases page. Expand the Assets list, for the version you need and find the extended version for your system.
For most users this will be:

hugo_extended_[VERSION]_linux-amd64.tar.gz

Step 2: Download it

Make sure is saved to your ~/Downloads folder.

Debian/Ubuntu Users

You can download the .deb file from the Assets list and install it with:
sudo dpkg -i hugo_extended_*_linux-*.deb

This works well, but the manual method below works on any Linux distribution.

Step 3: Install Hugo with a single Copy/Paste

This command block will create the necessary directory, extract the hugo binary into it, and make it executable.
It’s the simplest way to perform a local install.

Open a terminal and run this command block:

(
  set -e
  mkdir -p ~/.local/bin
  tar -xvzf ~/Downloads/hugo_extended_*.tar.gz -C ~/.local/bin/ hugo
  hugo version
)

Note: This command assumes you only have one Hugo tar.gz file in your Downloads/ folder. If it fails, make sure that’s the case.

Step 4: Add Hugo to your PATH (One-Time Setup)

The final step is to make sure your system knows where to find the hugo command. You may need to add ~/.local/bin to your system’s PATH.

Restart your terminal and run hugo version.
If it works: You’re done! Your PATH is already configured correctly.

If it says “command not found”: You need to edit your shell’s startup file.

Run the following command:

echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc

Then, restart your terminal or run source ~/.bashrc. (If you use Zsh, change .bashrc to .zshrc).

How to Update Hugo Manually

Updating is now simple. Just repeat the process:

  1. Download the new version from GitHub.
  2. Run the same single command block from Step 3. It will overwrite the old hugo binary with the new one.

The hugo-install Script

For my own use, I wrapped this entire process into a small Bash script.

It does the same thing as the commands above, but it’s a bit smarter. It checks for errors and also gives you the option to install Hugo globally for all users.

The Fast One-Liner

This is the fastest way. This command downloads the script, names it hugo-install, and makes it executable, all in one go.

curl -s -o hugo-install https://alpha.oxypteros.com/downloads/hugo-install && chmod +x hugo-install && echo "Script 'hugo-install' downloaded to: $(pwd)/hugo-install"

After downloading, you can inspect the script with cat hugo-install or open it in a text editor to see exactly what it does before running it with ./hugo-install.

The Manual Way

If you prefer, you can create the file yourself. Save the code below as hugo-install, make it executable with chmod +x hugo-install, and run it.

#!/bin/bash
# A script to safely install the latest version of Hugo on Linux.
# It's designed to be run after downloading the .tar.gz file from GitHub.

# Exit immediately if a command exits with a non-zero status.
set -e

# Configuration & Colors
GREEN='\e[32m'
RED='\e[31m'
YELLOW='\e[33m'
BOLD_GREEN='\e[1;32m'
RESET='\e[0m'

# Path Variables
DOWNLOADS_DIR="$HOME/Downloads"
LOCAL_INSTALL_DIR="$HOME/.local/bin"
GLOBAL_INSTALL_DIR="/opt/hugo"
GLOBAL_SYMLINK_PATH="/usr/local/bin/hugo"

# Main Script
echo -e "${BOLD_GREEN}Starting Hugo Installer...${RESET}"

# Find the Hugo archive.
echo "Searching for 'hugo_*_linux-*.tar.gz' in '$DOWNLOADS_DIR'..."
HUGO_ARCHIVES=$(find "$DOWNLOADS_DIR" -maxdepth 1 -name "hugo_*_linux-*.tar.gz")
ARCHIVE_COUNT=$(echo "$HUGO_ARCHIVES" | grep -c .)

# Handle cases with zero or multiple archives found.
if [ "$ARCHIVE_COUNT" -eq 0 ]; then
  echo -e "${RED}Error: No Hugo archive found.${RESET}"
  echo "Please download the 'hugo_*_linux-*.tar.gz' file from GitHub Releases and place it in your '$DOWNLOADS_DIR' folder."
  echo "URL: https://github.com/gohugoio/hugo/releases"
  exit 1
elif [ "$ARCHIVE_COUNT" -gt 1 ]; then
  echo -e "${RED}Error: Multiple Hugo archives found.${RESET}"
  echo "Ensure there is only ONE 'hugo_*_linux-*.tar.gz' file in your '$DOWNLOADS_DIR' folder and run this script again."
  echo -e "Found files:\n$HUGO_ARCHIVES"
  exit 1
fi

echo -e "${GREEN}Found Hugo archive: $(basename "$HUGO_ARCHIVES")${RESET}"

# Ask the user for installation type.
read -p "Install Hugo for the current user only (local)? [Y/n] " -n 1 -r
echo 

if [[ $REPLY =~ ^[Nn]$ ]]; then
  # Global Installation
  echo -e "\n${BOLD_GREEN}Starting global installation...${RESET}"
  echo "Hugo will be installed in '$GLOBAL_INSTALL_DIR' and symlinked to '$GLOBAL_SYMLINK_PATH'."

  # Create destination directory if it doesn't exist.
  if [ ! -d "$GLOBAL_INSTALL_DIR" ]; then
    echo "Creating destination directory (requires sudo)..."
    sudo mkdir -p "$GLOBAL_INSTALL_DIR"
  fi

  # Extract directly into the destination directory.
  echo "Extracting 'hugo' binary to '$GLOBAL_INSTALL_DIR' (requires sudo)..."
  sudo tar --no-same-owner -xvzf "$HUGO_ARCHIVES" -C "$GLOBAL_INSTALL_DIR" hugo
  sudo chmod +x "$GLOBAL_INSTALL_DIR/hugo" 

  # Check and create symlink.
  if [ -L "$GLOBAL_SYMLINK_PATH" ]; then
    if [ "$(readlink "$GLOBAL_SYMLINK_PATH")" != "$GLOBAL_INSTALL_DIR/hugo" ]; then
        echo -e "${YELLOW}Warning: Existing symlink at '$GLOBAL_SYMLINK_PATH' points to a different Hugo installation.${RESET}"
        echo "You may need to remove it manually if you want to use this new version."
    else
        echo -e "${GREEN}Symlink already correctly points to the new installation.${RESET}"
    fi
  else
    echo "Creating symlink in /usr/local/bin (requires sudo)..."
    sudo ln -s "$GLOBAL_INSTALL_DIR/hugo" "$GLOBAL_SYMLINK_PATH"
  fi

else
  # Local Installation
  echo -e "\n${BOLD_GREEN}Starting local installation...${RESET}"
  echo "Hugo will be installed in '$LOCAL_INSTALL_DIR'."

  # Create destination directory if it doesn't exist.
  if [ ! -d "$LOCAL_INSTALL_DIR" ]; then
    echo "Creating destination directory: $LOCAL_INSTALL_DIR"
    mkdir -p "$LOCAL_INSTALL_DIR"
  fi

  # Extract directly into the destination directory.
  echo "Extracting 'hugo' binary to '$LOCAL_INSTALL_DIR'..."
  tar --no-same-owner -xvzf "$HUGO_ARCHIVES" -C "$LOCAL_INSTALL_DIR" hugo
  chmod +x "$LOCAL_INSTALL_DIR/hugo"

  # Check if the install directory is in the user's PATH and provide instructions if not.
  if [[ ":$PATH:" != *":$LOCAL_INSTALL_DIR:"* ]]; then
    echo -e "\n${YELLOW}IMPORTANT POST-INSTALL ACTION${RESET}"
    echo "To use hugo from your terminal, you need to add '$LOCAL_INSTALL_DIR' to your PATH."
    echo "Run the following command, then restart your terminal:"
    echo -e "${BOLD_GREEN}echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.bashrc${RESET}"
    echo "(If you use Zsh, change '.bashrc' to '.zshrc')"
  fi
fi

# Final Verification
echo -e "\n${BOLD_GREEN}Installation complete!${RESET}"
echo "Verifying Hugo version:"
# Use the full path to ensure we're running the one we just installed.
if [[ $REPLY =~ ^[Nn]$ ]]; then
    "$GLOBAL_SYMLINK_PATH" version
else
    "$LOCAL_INSTALL_DIR/hugo" version
fi

That’s it. Whether you do it manually or with the script, you now have a rock-solid Hugo installation that will serve you well.