Build a Hugo Website on Azure

Deploy a Hugo website to Azure, automate deployments with a CI/CD pipeline using Terraform and GitHub Actions, and configure a custom domain.

What is Hugo?

Hugo is a popular, open source static site generator written in Go, known for being extremely fast and flexible. It is used to build websites such as documentation sites, landing pages, and corporate sites by generating pre-built HTML, CSS, and JavaScript files from content written in Markdown.

This very site is built with Hugo! In this guide, I’ll walk you through deploying a Hugo website to an Azure Static Web App using Terraform, which, while more involved than alternatives like GitHub Pages, provides some more practical experience with IaC.

Prerequisites

Before you begin, ensure you have the following set up.

  • An Azure account with an active subscription. Make sure it has the Entra ID role Application Administrator, and Azure Owner role on the subscription.
  • A GitHub account.
  • Locally installed tools: Git, Azure CLI, and a code editor.
  • (Optional) A custom domain name purchased from any registrar.

Repository Setup

To streamline the process, a GitHub template is provided. This template contains all the necessary Terraform, GitHub Actions, and Hugo boilerplate files.

  1. Navigate to the GitHub template repository.
  2. Click Use this template and select Create a new repository.
  3. Provide a name and optional description, then click Create repository.
  4. Clone your new repository to your local machine.

Terraform Backend

Before Terraform can manage your Azure resources, we must create a secure, remote backend. This involves running a script to provision an Azure Storage Account for Terraform’s state and a Service Principal for authentication.

  1. Locate the file ./tools/azure-backend.sh in your repository.
  2. Open the file and configure the environment variables with your specific details.
1
2
3
4
5
6
7
RESOURCE_GROUP_NAME="hugo"
STORAGE_ACCOUNT_NAME="tfstatehugo<INSERT RANDOM NUMBERS>" # must be globally unique"
CONTAINER_NAME="tfstate"
LOCATION="" # e.g. "eastus" or "norwayeast"
APP_NAME="Hugo - GitHub Actions"
YOUR_GITHUB_ORG="" # e.g. "my-github-username" or "my-github-organization"
YOUR_REPO_NAME=""
  1. Run the script from your terminal. You’ll be prompted to log into Azure.
  2. After completion, the script will output several important values. Save these, as you will need them for the next steps.

GitHub Authentication

To allow secure communication between Azure and GitHub, you need to create two sets of credentials: a Personal Access Token (PAT) for Azure to update your repository, and GitHub secrets for your workflows to authenticate with Azure.

Create a Personal Access Token (PAT)

  1. Create a fine-grained PAT on GitHub.
  2. Grant it the following repository permissions:
    • Contents: Read and write
    • Workflows: Read and write
    • Secrets: Read and write
  3. Copy the generated token and save it securely.

Create GitHub Secrets

  1. In your repository settings, go to Secrets and variables > Actions.
  2. Create the following secrets using the values from the backend script output and the PAT you just created:
    • AZURE_CLIENT_ID
    • AZURE_SUBSCRIPTION_ID
    • AZURE_TENANT_ID
    • GH_PAT

Deploy Infrastructure

With authentication configured, you can now deploy the Azure infrastructure using Terraform. This is handled automatically through a pull request workflow.

  1. Create a new Git branch.
  2. Open ./infra/variables.tf and edit the default values. For now, leave the custom domain variables unchanged.
  3. Commit your changes and push the branch to GitHub.
  4. Create a pull request. The workflow will automatically run and show you a terraform plan in a PR comment.
  5. Review the plan. If it’s correct, merge the pull request to deploy the resources.
  6. After merging, a new workflow file and a new secret will be automatically added to your repository by Azure.

Deploy Hugo Website

The final step is to configure the deployment pipeline to correctly build your Hugo site from the src directory and deploy it to the new Static Web App.

  1. Create a new branch, ensuring you’ve pulled the latest changes from main.
  2. Find the new workflow file at ./.github/workflows/azure-static-web-apps-<RANDOM_ID>.yml.
  3. Modify the file to set the app_location and output_location.
1
2
3
4
5
6
...
action: "upload"
app_location: "src" # <--
api_location: ""
output_location: "public" # <--
...
  1. Commit the change, create a pull request, and merge it.
  2. Once the pipeline succeeds, your Hugo website will be live! Check the Azure Portal for the URL.

Hugo Configuration

Once your site is deployed, you can manage its content and configuration. Here’s how to work with your Hugo site’s structure.

  1. The hugo.toml file is the main configuration for your site. Here you can add your title, description, and generally change a bunch of settings.
    • NB! For your domain to work you’ll have to customize the baseURL to match your domain.
  2. ./content/page/*contains menu pages, like the About page.
  3. ./content/post/* contains your blog posts. Add a new directory for each post with an index.md file.
  4. At the top of your index.md file, add YAML frontmatter to configure the post. Here’s an example with most of the possible config:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
---
title: Example
slug: example # This is the name that will be in your URL, e.g. https://domain.tld/post/example
description: Hugo, the world's fastest framework for building websites
date: '2019-02-28'
lastmod: '2020-10-09'
categories:
    - Example
tags:
    - Example
aliases:
  - example
license: CC BY-NC-ND
image: cover.jpg # Resolution: 1000 px × 667 px
weight: 1 # You can add weight to some posts to override the default sorting (date descending)
menu:
    main: 
        weight: 1
        params:
            icon: user
---
  1. Below the frontmatter, write your content in Markdown.
  2. Push changes to the main branch to trigger an automatic deployment.

(Optional) Add a Custom Domain

If you wish to use your own domain, you’ll need to update the Terraform configuration and then validate the domain in the Azure portal.

  1. In ./infra/variables.tf, set add_custom_domain to true and custom_domain to your domain name.
  2. Commit, push, and merge these changes via a pull request.
  3. In the Azure Portal, find your Static Web App and go to the Custom domains section.
  4. Follow the instructions to add a TXT record (this is already created) and an A record to your domain’s DNS settings at your registrar.
  5. This is how the DNS configuration should look like at your registrar. This example uses Cloudflare:
  6. Wait for DNS propagation. This may take a few minutes to a few hours.

For more detailed information on Hugo and the theme used in the template, please refer to the official documentation.

All opinions are my own.
Built with Hugo
Theme Stack designed by Jimmy