Creating a blog using Hugo, GitLab pages, and a custom domain
Introduction
This post describes the steps I took to set up the present blog using Hugo, GitLab and a custom domain.
My main requirements were:
- To avoid a blogging platforms (such as Medium or Substack),
- A cost-effective solution, ideally free,
- A low-maintenance solution, easy to work with and to update.
- A well-established and well-documented solution, that is also still maintained.
- Finally, the ability to use a custom domain.
The last criteria I wanted is basically the right to be wrong, i.e., to be able to change part of the solution in case of need. This is also why I chose not to use any blogging platform.
Tools
My requirements being pretty common, I went for a Static Site Generator:
- Content is written using markdown files. It’s portable and can be read by humans.
- The output of a SSG produces is a HTML/JS/CSS site, which uses few resources and can be deployed virtually anywhere.
- A SCM such as
git
allows to keep track of changes.
I’ve heard about Hugo a few years ago, and I just went for it. I may consider alternatives such as Zola in the future.
For hosting, I evaluated the following options:
- GitHub pages
- GitLab pages
- Netlify
- My registrar, which provides hosting alongside the domain name registration
Using GitHub Pages or GitLab pages solution appeared for me the most practical solutions, because of git integration and their CI/CD feature. I finally chose GitLab, as the free tier supports public pages using a private repository.
Setup
The setup is quite straightforward, and involves following the Hugo Quick start and the GitLab Pages documentation.
Installing Hugo
Installing Hugo can be done using various methods, I just picked the easiest one for me from the documentation, using winget
:
winget install Hugo.Hugo.Extended
Create a git
repository
Creating a git
repository can be done either by initializing one locally then adding the remote origin:
git init
git remote add origin https://gitlab.com/jallal_brahimi/blog.git
git branch -M main
git push -uf origin main
Or by creating directly a remote repository with the GitLab web interface, and cloning it locally:
git clone https://gitlab.com/jallal_brahimi/blog.git
I later found that a Hugo template can be prepopulated from GitLab directly.
Finally, I’ve added a .gitignore
file to exclude files created by Hugo
that git
should ignore :
/public/
/resources/_gen/
/assets/jsconfig.json
hugo.log
hugo_stats.json
.hugo_build.lock
hugo.exe
hugo.darwin
hugo.linux
Create a Hugo website
Once the repository is set up, generating an empty web site is straightforward:
# Init a new Hugo project in blog folder
hugo new site blog
cd blog
# Specify a theme. it can be changed later
`git` submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke
echo "theme = 'ananke'" >> hugo.toml
# Add a new post
mkdir posts/blog-setup/
hugo new content posts/blog-setup/index.md
Choose a theme
Various themes are available for Hugo, showcased on the official website.
I wanted something minimalistic with a dark mode and eventually chose Archie](https://themes.gohugo.io/themes/archie/) over Bear Cub.
If a theme has already been selected, it can be removed
git rm themes/ananke/
rm -rf .git/modules/themes/ananke/
To set the new theme:
`git` submodule add https://github.com/athul/archie.git themes/archie
echo "theme = 'archie'" >> hugo.toml
Theme configuration is done by editing the hugo.toml
file:
baseURL = 'https://www.jallalbrahimi.com/'
languageCode = 'en-us'
title = "Jallal's blog"
theme = 'archie'
copyright = "© Jallal" # (CC BY-NC-SA 4.0)
pygmentsstyle = "monokai"
pygmentscodefences = true
pygmentscodefencesguesssyntax = true
# Generate a nice robots.txt for SEO
enableRobotsTXT = true
[params]
mode="dark"
useCDN=false
# subtitle = "Minimal and Clean [blog theme for Hugo](https://github.com/athul/archie)"
[[params.social]]
name = "Email"
icon = "mail"
url = "mailto:xxxx"
[[params.social]]
name = "LinkedIn"
icon = "linkedin"
url = "https://www.linkedin.com/in/jallal-brahimi-62abb224/"
[[menu.main]]
name = "Home"
url = "/"
weight = 1
[[menu.main]]
name = "All posts"
url = "/posts"
weight = 2
[[menu.main]]
name = "About"
url = "/about"
weight = 3
[[menu.main]]
name = "Tags"
url = "/tags"
weight = 4
Setup the CI/CD
Hugo documentation provides a script for publishing on commit.
I’ve updated the .gitlab-ci.yml
so that the latest version of Hugo is used. I’ve also added a new step to force the fetch of git
submodules, as this stopped working when I’ve changed the theme.
variables:
DART_SASS_VERSION: 1.70.0
HUGO_VERSION: 0.122.0
NODE_VERSION: 20.x
GIT_DEPTH: 0
GIT_STRATEGY: clone
GIT_SUBMODULE_STRATEGY: recursive
TZ: Europe/Brussels
image:
name: golang:1.20.6-bookworm
before_script:
- `git` submodule update --init --recursive
pages:
script:
# Install brotli
- apt-get update
- apt-get install -y brotli
# Install Dart Sass
- curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz
- tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz
- cp -r dart-sass/ /usr/local/bin
- rm -rf dart-sass*
- export PATH=/usr/local/bin/dart-sass:$PATH
# Install Hugo
- curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb
- apt-get install -y ./hugo_extended_${HUGO_VERSION}_linux-amd64.deb
- rm hugo_extended_${HUGO_VERSION}_linux-amd64.deb
# Install Node.js
- curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION} | bash -
- apt-get install -y nodejs
# Install Node.js dependencies
- "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
# Build
- hugo --gc --minify
# Compress
- find public -type f -regex '.*\.\(css\|html\|js\|txt\|xml\)$' -exec gzip -f -k {} \;
- find public -type f -regex '.*\.\(css\|html\|js\|txt\|xml\)$' -exec brotli -f -k {} \;
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Setup a custom domain
GitLab describes how to use a custom domain with Gitlab Pages in their documentation
First, a new domain/subdomain need to be created:
Gitlab provides then a key to identify the website. This key must be added as a TXT
entry in the DNS configuration (using the registrar UI) to confirm the domain’s ownership and allow GitLab to redirect requests to the relevant website.
Gitlab suggests to use an ALIAS
entry for the DNS configuration, but some registrars do not support this keyword. I’ve first considered to add a CNAME entry, but it turned out that the specification of DNS prevents to have A TXT
and CNAME
simultaneously.
In the end, I’ve just added an A
entry pointing to the 35.185.44.232
. Using the TXT entry, GitLab is able to redirect any query to the correct website.
I performed this configuration on the www
subdomain, but the same applies to the root domain. Another option can be - if the root domain is hosted - to use a .htaccess
file to redirect to the www
subdomain.
A nice perk with GitLab is that a Let’s Encrypt certificate can be generated and configured automatically.
Usage
# To create a new post
hugo new content posts/my-post/index.md
# To launch the hugo server in draft mode
hugo server -D