Building and Deploying a Blog with Hugo and GitHub Pages

In today's fast-paced digital age, where information is just a Google or ChatGPT search away, setting yourself apart is more important than ever. Blogging is one of the most effective ways to showcase your knowledge, contribute to the community, and build your brand. Think of it as a dynamic extension of your resume, a portfolio that grows and evolves with you. It provides a platform to share ideas, tackle complex problems, and express your viewpoints while delivering immense value to your readers.


Install Hugo

Hugo is a static site generator built with Go. It's popular for its speed, flexibility, and ease of use. Hugo Extended is a variant of Hugo that supports additional features like Sass/SCSS compilation, image processing, and more. Hugo Extended is Hugo but with some extra capabilities to help manage more complex projects. For more information, see the Hugo website.

Install Hugo Extended using your operating system's package manager. While the following aren't the only ways to install Hugo, they're how I installed it on each operating system I use. For additional options, see the installation section of the Hugo website.


Install Hugo Extended using the Windows Package Manager.

1winget install Hugo.Hugo.Extended --source winget

Install Hugo Extended


Use the Homebrew package manager to install Hugo Extended.

1brew install hugo

Install Hugo Extended


Use the package manager for your distribution to install Hugo Extended. The following example uses the APT package manager to install Hugo Extended on Ubuntu.

1sudo apt install hugo

Install Hugo Extended

Create a Hugo site

Using PowerShell, navigate to the parent directory on your computer's file system where you want to create your Hugo site. The examples in this article use the $HOME/Developer/git directory as the parent path.

1Set-Location -Path $HOME/Developer/git

Change to the parent directory where to create the Hugo site

Create a new Hugo site. The examples in this article use myhugosite for the Hugo site name.

1hugo new site myhugosite

Hugo new site

Navigate to the root directory of your newly created Hugo site.

1Set-Location -Path $HOME/Developer/git/myhugosite

Change to the new Hugo site directory

Create a Git repository

Initialize the site's directory as a Git repository.

1git init

Initialize a git repository

Add a .gitignore file

1$gitIgnore = @'
5Set-Content -Path $HOME/Developer/git/myhugosite/.gitignore -Value $gitIgnore -Force

Add a .gitignore file

Add a theme

Adding a theme to a Hugo website can enhance its aesthetics and functionality. Hugo offers various methods for installing and using themes. This article uses Hugo modules to add a theme to the site. See your theme's documentation for guidance on adding a theme to a Hugo site.

Initialize a new Hugo module. This initialization creates a go.mod file in the root of the site. The go.mod file tracks the dependencies of the site and the version of Go that the site uses. Hugo and GitHub Actions use the version of Go when building the site.

1hugo mod init<github-username>/myhugosite

Initialize a new Hugo module

Remove the Go patch version from the go.mod file. This modification is required because GitHub Actions only supports Go specified in Major.Minor format. GitHub Actions builds and deploys the site to GitHub pages and fails if the go.mod file includes the patch version. For more information about Semantic versioning, see

1Get-Content -Path ./go.mod -OutVariable content
2$content -replace '([^\.]*\.[^\.]*)\..*', '$1' | Out-File -FilePath ./go.mod -Force
3Get-Content -Path ./go.mod

Remove the Go patch version from go.mod

Add a theme to the Hugo site. This article uses the Relearn theme for Hugo.

1hugo mod get

Add a theme to the Hugo site

Configure Hugo to use the Relearn theme.

1Add-Content -Path hugo.toml -Value "theme = ''"

Configure Hugo to use the Relearn theme

Add content

Create a new post. The following example uses the name for the new post. The name of the post is the name of the file created in the content/posts directory.

1hugo new content posts/

Create a new post

Add content to the newly created post. You write the content for Hugo posts in Markdown. Markdown is a lightweight markup language that you can use to add formatting elements to plaintext text documents. Title and date are the only metadata elements specified in the following example. See the documentation for your specific theme to determine the supported metadata.

 1$post = @'
 3title: "My First Article"
 4date: 2023-10-26T06:30:00-05:00
 7Insert lead paragraph here.
 9> Block quote
15Unordered list
17- first
18- second
19- third
21Ordered list
231. first
241. second
251. third
27Inline code `Get-Help`.
29Cmdlet name `Get-Help`.
31Parameter name **ParameterName**.
33Parameter value `$true`.
35Set-Content -Path $HOME/Developer/git/myhugosite/content/posts/ -Value $post

Add content to the post

Build the site locally

Start the Hugo server locally on your computer. Hugo serves the site locally so that you can view a live preview in your web browser. The Hugo server watches for changes to the site and automatically rebuilds it when changes are detected.

1hugo server

Start the Hugo server

Consider starting hugo server as a background job because otherwise, it ties up your PowerShell prompt or session.

1Start-Job -Name hugo -ScriptBlock {hugo server} -WorkingDirectory $HOME/Developer/git/myhugosite

Start Hugo server as a background job

Visit the local site in your default web browser. Port 1313 is the default, but a different port is dynamically assigned if the default port is already in use.

1Start-Process http://localhost:1313/

Open the local site in your default web browser

The website should look similar to the following.

Visit the local site in your web browser

Stop the Hugo server by pressing CTRL+C in the PowerShell console or by stopping the PowerShell job, depending on how you started it.

1Get-Job | Stop-Job -PassThru | Remove-Job

Stop the Hugo server

Create a repository on GitHub

Add and commit the changes to your local Git repository.

1git add .
2git commit -m 'Initial commit'

Add and commit the changes

Tip: To authenticate with GitHub using the GitHub CLI, run gh auth login.

Create a new repo on GitHub using the GitHub CLI and push your local clone to it. You can only use GitHub Pages with a public repo if you're using a free GitHub account. Private repos with GitHub Pages require a paid GitHub account.

1gh repo create myhugosite --public --push --source=. --description 'My hugo website'

Create a new GitHub repo and push to it

Deploy the site to GitHub Pages

Enable GitHub Pages and set it to deploy via GitHub Actions. Use the GitHub CLI with the GitHub API to update the repo settings.

1gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" /repos/<github-username>/myhugosite/pages -F "source:branch=main" -F "source:path=/" -F "build_type=workflow"

Enable GitHub Pages and deploy via a GitHub Action

Add the GitHub Actions workflow folder locally.

1New-Item -Path $HOME/Developer/git/myhugosite/.github/workflows -ItemType Directory -Force

Create workflow folder

Save the following GitHub Actions workflow to a file named Hugo.yaml in the workflow folder. Run hugo version on your computer to determine what version of Hugo you're using and update the version in the workflow. The following workflow is a modified version of the one found in the Host on GitHub Pages section of the Hugo website.

 1$workflow = @'
 2# Sample workflow for building and deploying a Hugo site to GitHub Pages
 3name: Deploy Hugo site to Pages
 6  # Runs on pushes targeting the default branch
 7  push:
 8    branches:
 9      - main
11  # Allows you to run this workflow manually from the Actions tab
12  workflow_dispatch:
14# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
16  contents: read
17  pages: write
18  id-token: write
20# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
21# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
23  group: "pages"
24  cancel-in-progress: false
26# Default to bash
28  run:
29    shell: bash
32  # Build job
33  build:
34    runs-on: ubuntu-latest
35    env:
36      HUGO_VERSION: 0.119.0
37    steps:
38      - name: Install Hugo CLI
39        run: |
40          wget -O ${{ runner.temp }}/hugo.deb${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
41          && sudo dpkg -i ${{ runner.temp }}/hugo.deb
42      - name: Install Dart Sass
43        run: sudo snap install dart-sass
44      - name: Checkout
45        uses: actions/checkout@v3
46        with:
47          submodules: recursive
48          fetch-depth: 0
49      - name: Setup Pages
50        id: pages
51        uses: actions/configure-pages@v3
52      - name: Install Node.js dependencies
53        run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
54      - name: Build with Hugo
55        env:
56          # For maximum backward compatibility with Hugo modules
57          HUGO_ENVIRONMENT: production
58          HUGO_ENV: production
59        run: |
60          hugo \
61            --gc \
62            --minify \
63            --baseURL "${{ steps.pages.outputs.base_url }}/"
64      - name: Upload artifact
65        uses: actions/upload-pages-artifact@v1
66        with:
67          path: ./public
69  # Deployment job
70  deploy:
71    environment:
72      name: github-pages
73      url: ${{ steps.deployment.outputs.page_url }}
74    runs-on: ubuntu-latest
75    needs: build
76    steps:
77      - name: Deploy to GitHub Pages
78        id: deployment
79        uses: actions/deploy-pages@v2
81Set-Content -Path $HOME/Developer/git/myhugosite/.github/workflows/hugo.yaml -Value $workflow -Force

Add the GitHub Actions workflow

Add, commit, and push the changes. Pushing the changes to the main branch of your GitHub repo triggers the GitHub Actions workflow. The GitHub Action is a build-and-deploy workflow that builds and deploys the site to GitHub Pages. The GitHub Actions workflow automatically runs when you update the main branch, and you can manually trigger it by running gh workflow run 'Deploy Hugo site to Pages'.

1git add .
2git commit -m 'Added GitHub Action workflow'
3git push

Add, commit, and push the changes

You can see the name of all your GitHub Actions workflows using the GitHub CLI.

1gh workflow list

List all your GitHub Actions workflows

Check the status of the GitHub Actions workflow. It may take a few minutes for it to complete.

1gh workflow view 'Deploy Hugo site to Pages'

View the status of the GitHub Actions workflow

Tip: The workflow failed if you didn't remove the Go patch version from the go.mod file, as described earlier in this article.

Once the GitHub Actions workflow completes successfully, visit the public website in your default web browser.

1Start-Process https://<github-username>

Start-Process public website

The website should look identical to the local one you previewed and similar to the following.

Visit the public website

Clean up resources

If the resources created in this article aren't needed, you can delete them by running the following commands.

Warning: The following commands perform non-recoverable destructive changes. If resources outside the scope of this article exist in the specified local directory or GitHub repo, these commands also delete them.

The following commands delete the site, GitHub repo, and local directory used in this article. I've placed a comment symbol before each command so you don't run them accidentally.

Tip: Deletion requires authorization with the delete_repo scope. To authorize, run gh auth refresh -s delete_repo.

1# Get-Job -Name hugo -ErrorAction SilentlyContinue | Stop-Job -PassThru | Remove-Job
2# Remove-Item -Path $HOME/Developer/git/myhugosite -Recurse -Force
3# gh repo delete myhugosite --yes

Cleanup resources


This article outlined how to set up a free blog using Hugo and GitHub Pages. It covered essential prerequisites, installation, and content creation. It also provided step-by-step instructions for local testing and online deployment through GitHub.

While outside the scope of this article, I recommend using a custom domain name for your blog in case you decide to move it somewhere else; you can maintain the existing URLs for your content. It's also easier to remember and share with others.