Building a website with Hugo and GitLab Pages


This site has been built with Hugo and the hugo-coder theme, and is hosted on GitLab Pages. This post has some notes on these tools.

About Hugo

Hugo is a “static site generator”, which means that it prepares pages in advance of serving them. This is in contrast to a “dynamic site generator”, which generates pages “on the fly” for every single page request. Serving static pages has many advantages, particularly speed, and there isn’t really any particular need to generate content such as this dynamically anyway.

Content is prepared in Markdown, and the templates which change how a site looks are called Hugo Themes. Separating content from presentation in this way has many advantages, e.g. you can (at least in theory) redesign your site without having to change any content, or present the same content differently to different devices, or even potentially move to a different static site generator without having to rewrite your content.

There are alternatives to Hugo such as Jekyll. I chose Hugo because it sounded good, and I haven’t had any issues with it so I’ve stuck with it.

About GitLab

GitLab is a repository hosting service for the git distributed version-control system. Using the git version-control system to manage your content and other site assets is an advantage for software developers because git is a tool they’re likely to be familiar with already. GitLab Pages provides free hosting for static content, such as content generated via a static site generator like Hugo.

The Markdown files are stored in a content folder, and changes are pushed to the repository. GitLab also has a continuous integration pipeline which is triggered by a push, and this builds the site and copies it to GitLab Pages, from where it is served.

There are alternatives to GitLab Pages such as GitHub Pages. I chose GitLab because it offered private repositories and continuous integration, although GitHub now has these too.

Installing and using git

Installing git and configuring ssh

On Ubuntu:

sudo apt install git

Username and email are configured via:

git config --global "<username>"
git config --global "<email>"

Connectivity is easier if ssh is set up. This means that instead of entering commands like git clone<repositorypath>, which will then prompt you for username and password, you’ll use git clone<repositorypath>, and not be prompted for username and password. To set this up, first generate a key, e.g. via:

ssh-keygen -t ed25519

Then select location (e.g. default at /home/<username>/.ssh/id_ed25519) and passphrase if you want. Finally, as per GitLab via ssh, copy this to your GitLab account via Settings / SSH keys, and test via:

ssh -T

Setting up the git repo

To upload an existing directory, e.g. containing your MarkDown files, to GitLab:

  • create the GitLab project you want to save it in via the web interface
  • change to the directory you want to add
  • initialise git in that directory
  • then add, commit and push:
cd /home/<username>/<project>/
git init
git remote add origin<repositorypath>
git add .
git commit -m "Initial commit"
git push -u origin master

If you find you’ve uploaded files that you don’t need or want in version control, add their details to .gitignore (which is normally part of the repo and so potentially visible to others) or .git/info/exclude (which is not part of the repo and so not visible to others) so they aren’t subsequently readded to git, then remove from git via:

git rm --cached <filename>

and then commit and push. The files will won’t be deleted from the local file system.

To add files:

git add <filename>

and then commit and push.

Installing and configuring Hugo

Setting up a site

This is a summary of the Hugo Quick Start for Ubuntu 18.4.

Download and install the latest release from Hugo Releases. I found I needed to install Hugo extended, i.e. hugo_extended_<version>_Linux-64bit.deb, to avoid TOCSS errors with some themes such as hugo-coder.

To create the site:

hugo new site <sitename>
cd <sitename>
git init

And add a theme, e.g. the Hugo Coder theme:

git submodule add themes/hugo-coder
echo 'theme = "hugo-coder"' >> config.toml
hugo new posts/

And edit accordingly.

Note that the Markdown files will typically have some metadata at the start of the file, e.g.

title: "Building a website with Hugo and GitLab Pages"
date: "2019-11-08"
draft: false

To view, start the local dev server:

hugo server -D

And view on http://localhost:1313/ .

Configuring continuous integration

As per Host on GitLab, change to the site location, touch .gitlab-ci.yml and edit accordingly.

Note that I saw some examples using as the image, and I tried this because it sounded more “official”, but when using some themes such as hugo-coder this gave TOCSS errors in the build, so I reverted to the monachus/hugo image which must be Hugo extended with TOCSS support.

I’ve added /public and /resources to .gitignore because these are system generated and so there’s no benefit to managing in git:

echo "/public" >> .gitignore
echo "/resources" >> .gitignore
git add .
git commit -m "Added /public and /resources"

To push and trigger the site build and deploy:

git push -u origin master

As per earlier comment, if you have set up ssh and the remote origin<yourrepo>, then this will not require a username and password.

The build status will show in your project pipeline, and once successfully built, assuming no errors, you should see at https://<username><sitename>/.

Configuring DNS

In my case I wanted to point an existing domain to the Hugo site. GitLab has some documentation on how to Add your custom domain. In my case, I made the following main changes:

  • I went to my domain provider and added the A record to

  • In GitLab, I went to Settings / Pages, and added the domain.

  • Back in the domain provider interface I need to add the TXT record to verify the domain.

  • Once verified, I enabled SSL, and it sorted the certificate from Let’s Encrypt automatically, and then I selected “Force HTTPS”.

  • In GitLab, in Settings / General / “Visibility, project features, permissions” for the project, I made Pages visible to Everyone.

Customising a theme

I experimented with a few themes, before settling on Hugo Coder.

There are a couple of things I couldn’t see initially how to do.

The first was adding a link to Hacker News. Icons seem to come from Font Awesome and I think you can pretty much add any icon from there with the right identifier along with any link in the [[]] section of config.toml, e.g.:

	name = "Hacker News"
	icon = "fab fa-hacker-news"
	weight = 1
	url = "<userid>"

The second was adding a Table of Contents (toc) to the start of every page. There is a reference to this on the Table of Contents . What I did was copy themes/hugo-coder/layouts/posts/single.html to layouts/posts/single.html and add {{ .TableOfContents }} just before {{ .Content }}. Need to remember to update this if updating the theme source, or remove this if changing to a different theme.

Note that if adding other themes, some theme documentation suggests a git clone<repositorypath>, but if you are already inside a git project it is better to use a git submodule add<repositorypath> <themename>. Submodules can be updated at a later date via git submodule foreach git pull origin master, noting that customisations in layouts/ may need to be redone if the source files change.

Adding IndieWeb support

In addition to the template customisations above, I’ve since made some further template changes to have an h-card and so I can login with IndieAuth:

  1. Add a relme to the [Params] in config.toml, so the value can be configured rather than hardcoded.
  2. Copy themes/hugo-coder/layouts/partials/home.html to layouts/partials/home.html. Note comments above about updating this if updating the theme source.
  3. In layouts/partials/home.html make the following 3 changes:


<div class="about">


<div class="about vcard h-card">


<div class="avatar"><img src="{{ . | relURL }}" alt="avatar"></div>


<div class="avatar"><img class="photo" src="{{ . | relURL }}" alt="avatar"></div>


    <h1>{{ }}</h1>
    <h2>{{ }}</h2>


    <h1 class="fn">{{ }}</h1>
    <h2>{{ }}</h2>
    <a href="{{ .Permalink }}" rel="me" class="u-url u-uid"></a>
    {{ if .Site.Params.relme }}
    <link rel="me" href="{{ .Site.Params.relme }}" />
    {{ end }}
    <link rel="authorization_endpoint" href="">

Normal workflow for making edits to the site

Once the site is all set up, installed and running, the normal workflow is to:

  1. Pull latest remote changes:
git pull origin master

Note that this step is only necessary if it is a shared project (i.e. other people potentially working on it), or if you have been working on it on multiple devices.

  1. Start the local web server:
hugo server -D

And view the site at http://localhost:1313/ .

  1. Edit MarkDown files locally. When you save the files the changes will reload on your browser automatically, which is nice.

  2. Once you’re ready to publish, change draft: true to draft: false in the Markdown if necessary, and do the usual git add, commit and push:

git add .
git commit -m "Description of change"
git push -u origin master
  1. Check on the published web site. You may need to refresh your browser since static content is typically cached.

Bonus points - editing and publishing on a mobile phone

On an Android device, install termux, and in the terminal:

apt update
apt upgrade
apt install openssh
apt install git
git config --global "<username>"
git config --global "<email>"
cd storage/shared
mkdir projects
cd projects
git clone<repositorypath>

On my device, storage/shared is /data/data/com.termux/files/home/storage/shared which is a symlink to /storage/emulated/0/shared, so you can find the files in there. Then you can use your usual editing and publishing workflow (minus the previewing), i.e. before starting an editing session ensure the local files are up-to-date with git pull origin master, edit with your Android text editor (I’m currently using Markor), and publish with git add ., git commit -m "Comment", and git push -u origin master.

Its a bit awkward from the command line, but you could make it slightly easier by setting up ssh similar to the desktop process, and there are other more integrated tools although I can’t vouch for them. At least it works fine for quick edits you don’t need to preview.


So there it is. Not hugely complex, although admittedly not something I’d recommend a non-technical person try.

Oh, and source is at GitLab Michael I Lewis web site.