Creating a Hugo site from scratch, when knowing nothing about Hugo can be a pain. Here we will go through the steps needed, and try to explain everything we do.

Intro

Aim

At the end of this article, we will have a static Hugo website, with a nice theme, and with Continuous Integration enabled, for automatic publishing of new contents through sFTP.

This documentation was first written to share the knowledge between authors of this blog. I think it can also be useful for other people but you will have to adapt it to your needs.

Requirements

  • A place to host a static website
  • A GitLab account on any GitLab instance (we use https://0xacab.org)
  • Terminal skills
  • Basic Git and GitLab knowledge

Environment

We will work on Debian GNU/Linux, version 12 (Bookworm). We need to install hugo (not from APT) and git (from APT). We will also use a Gitlab instance to store our source code and automatically publish content we push to the repo.

Getting hugo using Podman or Docker

Depending on the theme you choose, you might need a more recent version of Hugo than the one available in Debian. As I’ve chosen PaperMod, I always need a modern Hugo version, which Debian doesn’t provide, so I take hugo from a docker image.

  • Install podman: sudo apt install podman
  • Add a shell alias to use hugo from podman: in ~/.bashrc, add: alias hugo="podman run --rm -it -p 1313:1313 -v ./:/src docker.io/hugomods/hugo:std-base"

After closing and reopening the terminal, run hugo and see if it works. Navigate to a hugo source and try to build. To update hugo, run podman pull docker.io/hugomods/hugo:std-base.

Let’s do it

Create a new site and have it tracked by Git

  • This will create a folder called ‘mysite’ with default Hugo files:
    hugo new site mysite
    
  • Initialize the website repo:
    cd mysite
    git init
    # Let's call the branch "main" instead of "master"
    git branch -m main
    git add .
    git commit -m "New hugo site"
    
  • Add a remote, use it by default and push to it:
    git remote add origin ssh://mygitlabinstance/mysite/mysite
    git push --set-upstream origin main
    

Theme stuff

First thing will be choosing a theme. Pretty dramatic thing to do. I’ve chosen PaperMod because it doesn’t call make external calls, it supports multiple authors (but no author box), and is multi-language.

  • Once we’re settled with a theme, lets add it as a submodule of our Git repo. I recommend reading about submodules, seems like they can be picky to use.
    git submodule add https://github.com/adityatelange/hugo-PaperMod/ themes/papermod
    
    (adapt the link to your need)
  • Index the new folder containing our submodule. From the git website repo point of view, this folder is actually a file. See git status, git diff or git log -p. This file contains a reference to the HEAD commit of the submodule. When pulling the website repo, git will checkout the submodule to this commit. So we have to commit changes to that file when the submodule changes.
    git add themes/papermod
    
  • Index the .gitmodule file, which holds submodules config:
    git add .gitmodules
    
  • Commit!
    git commit
    

Make changes to the theme

Layout changes

If one wants to make changes only to the files in the layout/ theme directory, best solution is to copy these files to the layout/ directory of the website and change things there.

All kind of changes

To make broader changes to a Hugo theme, we must set up the submodule so that we can make changes to it, and push these changes to a repo we own.

Prepare the repo

  • First thing will be to rename the remote origin of the submodule repo to upstream, and add our own new repo as origin. This new repo will only contain the theme and modifications we made to it. This repo must be public if you want others to clone your main repo.
    cd themes/papermod
    git remote rename origin upstream
    git remote add origin ssh://gitprovider.net/me/papermod
    
  • And let’s set our repo as default, and push the theme to our repo:
    git push --set-upstream origin
    
  • Now we have to change the submodule URL in our main repo. First thing is going back to our repo’s root, then we tell git to change the submodule URL to our new repo.
    cd ../..
    git submodule set-url themes/papermod ssh://gitprovider.net/me/papermod
    
    Note: the “themes/papermod” part must exactly match what’s in .gitmodules.
  • Now that we’ve changed the submodule’s URL, we have to commit that.
    git add .gitmodules
    git commit
    

Do the modifications and push them

This part wont be detailed, but:

  • make your modifications
  • commit them locally
  • push them to the theme-dedicated repo created before
  • commit the main repo submodule change (so that the main repo knows what commit of the submodule to use, when cloning the main repo for example)
  • push main repo

Update theme

When using the theme upstream repo as submodule URL

Updating the theme should be pretty easy.

  • Go to the theme directory, and have git apply updates:
    cd themes/papermod
    git pull --ff-only
    
  • If you have files in the website layout/ folder, you may check they are still compatible with the theme new version.
  • Commit changes on the main repo
    cd -
    git add themes/papermod layouts/
    git commit
    

When using our own repo to store the theme

  • Go to the theme directory, pull changes, and replay our local commits (if any) on top:
    cd themes/papermod
    git pull --rebase upstream master
    
  • Merge conflicts if any
  • Fix local changes in the main repo’s layout/ if needed
  • Push changes to our repo of the theme:
    git push
    
  • Update the website repo with the up-to-date commit reference:
    cd ../..
    git add themes/papermod
    git commit
    
  • Push our main repo
    git push
    

Publish content

  • Read Hugo documentation! I recommend starting with https://gohugo.io/getting-started/directory-structure/ and follow the links.
  • Read your theme documentation!
  • Hugo uses Sections, meaning folders in content/. In the config file we can set mainSections, these will be the sections displayed when listing posts on the frontpage for example.
  • To create a new article or page in the posts section, we can use the hugo command:
    hugo new posts/new-article.md
    
    Hugo will create content/posts/new-article.md based on a template. It will decide which template to use using the section of our new article (ie. posts). Templates can be found in the archetypes/ directories of the website and the theme. In this example, Hugo will try to find posts.md and use it as the template, and default to _default.md or Hugo default’s template.
  • Commit and push!

Use Gitlab CI/CD to automatically push changes to the website

In our case, we use sFTP to transfer file to our website host.

Configure GitLab

  • In our Gitlab project we have to enable CI/CD. Follow GitLab’s documentation.
  • In the project Settings, CI/CD, Variables: add variables you don’t want to publish in your repo, such as sftp_host, sftp_port, sftp_user, and sftp_pass.

Configure CI/CD actions in our repo

  • All the config happens in .gitlab-ci.yml:
    image: alpine:latest
    deploy:
        variables:
          # Have git fetch the theme submodule
          GIT_SUBMODULE_STRATEGY: recursive
    
        # we don't use stages but GitLab CI/CD wants a stage so let's use anything
        stage: deploy
    
        # Only fetch the 'main' branch
        only:
        - main
    
        # Commands to execute in the container
        script:
          # Install lftp for sFTP transfers, hugo for building the website,
          # ca-certificates to be able to verify our hosts' cert.
        - apk add lftp hugo ca-certificates
          # Build the website
        - hugo
          # Transfer changed files
        - lftp -c "set ftp:ssl-allow on; open -u $sftp_user,$sftp_pass $sftp_host; mirror -Rev public/ ./www --parallel=10 --exclude-glob .git* --exclude .git/"
    

Push changes!

  • Using git push will now trigger a job on the GitLab instance that will automatically publish changes to the website!