How to Host a Hugo Site on GitLab
The technical part of how this site came into being.

In technical gibberish, this post is about how you can use GitLab Pages to host a static website, generated by Hugo, on gitlab.io, for free. There probably already are thousands of posts on the same topic, including the official documentations from Hugo and GitLab.

As always is the case, despite of their excellent documentations, nothing ever worked on the first try. Eventually I managed to deploy this blog on GitLab, but not without deleting everything to do a fresh restart and some desperate Google sessions.

If you are here just to copy some quick commands, jump to the end section.

Why

I’m saving the lengthy reflections on why one should blog like this for another post. In short, deploy your personal blog to GitLab is a fun adventure. Also, once it’s done, the writing process is seamless like magic, very suitable to be the first real post of this grimoire.

How

Always start with the official guides. Here is Hugo’s Quick Start, and here is GitLab’s. They are mostly fine except for a few caveats (just some details easy to miss), which is the main reason why I’m recording my steps here.

For an overview, we first install Hugo locally, then use it to generate some files on the local computer. Then we write the content of the blog by editing the .md files generated by Hugo on demand. All these files can later be used by Hugo to generate the web pages for a real website, which is a process called ‘build’. Traditionally these built files can be uploaded to a static website hosting service to work as a website. But instead of the traditional way, we use Git to upload all the files (before building) to GitLab, then tell GitLab to generate and host the website for us.

Prerequisites

A computer with proper internet connection, a GitLab account, a terminal environment with Git installed and set up. If you follow the commands carefully, this guide does not need prior experience with Git. But some courage and perseverance are definitely needed if you are not familiar with Git.

As to the computer and its operating system, almost every platform works. But in my case it is WSL2 with Ubuntu-20.04 on Windows 10.

Install

To install Hugo, various ways were listed for different platforms, including a detailed video guide for Windows users. For Docker (and Podman) users, you can also check this post on how to use Hugo from containers, without installing it on the host. But I strongly recommend all Windows 10 (and 11) users to use WSL2 for this task, and just pretend yourselves as Linux users. For example, I used the good old apt in WSL Ubuntu, which was very straightforward as it should be.

sudo apt update
sudo apt install hugo

The part not very clear in the official guide was whether you need the ’extended’ version. I suggest using it because some themes may need it, and it only takes a little more space. However it doesn’t really matter in this case, because, as said above, GitLab will take care of the building and serving part.

Once installed, verify the version by:

hugo version

I got:

Hugo Static Site Generator v0.68.3/extended linux/amd64 BuildDate: 2020-03-25T06:15:45Z

So it’s version 0.68.3, extended. Not the latest, but fully functional.

Generate

The Site

Since hugo version worked fine, now is the time to prepare the site. In your terminal, cd to a place where you want to put the site files (e. g., ~/my-short-lived-projects/), then you need to decide the name of your site, which also depends on what URL you want it to look like.

For GitLab pages, every user can have a special site located at <username>.gitlab.io, and unlimited number of sites at <username>.gitlab.io/<site-name>, where the <username> must be your username. If you want the former one, you need to create a repository called <username>.gitlab.io, else you can just create the repo with the name blog (once deployed, it will become <username>.gitlab.io/blog, which also makes sense for a personal blog). In my case, as you already know, I went with felixsmli.gitlab.io so that is also my repo name.

hugo new site <repo-name>

This is where Hugo generates stuff for you. Example response is something like this:

Congratulations! Your new Hugo site is created in ~/my-short-lived-projects/<repo-name>.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from https://themes.gohugo.io/ or
   create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
   with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".

Visit https://gohugo.io/ for quickstart guide and full documentation.

What actually happened was that a folder with the specified <repo-name> is created, with some files and more folders. Enter it and have a look:

cd <repo-name>
ls

Typically there will be a config.toml file, and some other folders like archetypes, content, etc. Edit the config.toml file to include the URL you just decided for your blog. If it’s <username>.gitlab.io, just change the baseURL part accordingly to:

baseURL = "https://<username>.gitlab.io/"

Alternatively if you want to save the main domain for something else, and prefer this site on <username>.gitlab.io/<repo-name>, also add a new line on relativeURLs.

baseURL = "https://<username>.gitlab.io/<repo-name>/"
relativeURLs = true

If you forgot to do this before you install the theme or create posts, the theme assets and the posts may fail to load because they all got wrong URLs.

Now it is time you initialize this whole folder as a Git repository:

git init

The Theme

Find a theme that you like from the address shown. While choosing, note that they typically declare a Minimum Hugo Version, find one that your current Hugo version satisfies.

My chosen theme was Diary. Install was simple with Git:

git submodule add https://github.com/AmazingRise/hugo-theme-diary.git themes/diary

This creates a Git submodule located at ./themes/diary, which will be available for you future site to use as a theme. To actually use it, you need to edit the config.toml file to include a line theme = "diary".

echo 'theme = "diary"' >> config.toml

You can also do more theme specific edits to the file according to their Wiki, or just copy sections from their example (don’t overwrite the changes on baseURL you just made with their example!).

Now you can have a preview of the empty site by:

hugo server

By default, the site will be served at http://localhost:1313/, which is only available to you. Go to this URL in the browser, you should be able to see a mostly empty site (with a name My New Hugo Site on the top, and some copyright information of the theme on the lower left).

The First Post

Go back to the terminal and press Ctrl+C to stop the preview. Create a post to say hello:

hugo new posts/hello-world.md

This creates a file with its location printed to your terminal. Edit it like a regular Markdown file to include whatever you want to say. Note that, unlike usual Markdown files, there is a special header:

---
title: "Hello World"
date: 2021-09-28T13:02:56+08:00
draft: true
---

This is called the Front Matter of the post. Just write your post content below it. Also note that there is draft: true, which means that this post is a draft. Draft posts are not shown when you do regular hugo server. For preview, you need to tell Hugo to show it:

hugo server -D

Or you can write your post properly and change it to draft: false.

Deploy

Now that you have everything needed for your new blog locally, it’s time to prepare it for deployment. Traditionally, you use the main command hugo to generate all the files needed for the website in a directory public, then you will need to upload all of its content to somewhere for hosting the actual site. However, with GitLab this works slightly different, because GitLab Pages natively supports Hugo through their CI/CD function.

What we’ll do is to commit the whole directory as a local git repository, include a magical file so that GitLab knows what to do with it once we pushed the repo online, wait for a few seconds, and see our site.

Prepare

First make sure you are still at the project root directory, ~/my-short-lived-projects/<repo-name> in our example case. There are three more files to be modified manually.

  1. config.toml
  2. .gitlab-ci.yml
  3. .gitignore
config.toml

In The Site part and The Theme of this guide we edited this file. Now is time to double check for additional things like title.

.gitlab-ci.yml

For GitLab to build the site, it needs to get all the files in this directory, as well as a special file called .gitlab-ci.yml (yes, the filename starts with a dot), with the following content:

image: registry.gitlab.com/pages/hugo:latest

variables:
  GIT_SUBMODULE_STRATEGY: recursive

pages:
  script:
  - hugo
  artifacts:
    paths:
    - public
  only:
  - master

Create the file and copy all these cryptic content in the above block to it. It’s very magical, do not mess with it unless you know what you are doing. Also note that for the image, it uses latest instead of a specific version. This means that it will always use the latest version of Hugo to build your site, which can be a problem when your local version of Hugo (or the Hugo version the author of your theme used) is too old1. The solution is to upgrade your local Hugo from time to time, or fix the image version by picking one from this list.

.gitignore

Since GitLab will be creating the content under public on their side, we’d better tell Git to ignore it (if we accidentally entered the command hugo and generated the site files). Do this by adding the directory to the .gitignore file:

echo "/public" >> .gitignore

Commit

Now, finally, all the files are ready. Commit all your changes with some really creative initial commit message:

git add .
git commit -m "Initial commit"

If this is your first time using Git, or you haven’t configured it yet, Git will complain:

*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"

to set your account's default identity.
Omit --global to set the identity only in this repository.

Do as what it demands, then run the git commit line again. This is when you submit your soul to the Great Git God and start your eternal suffering with modern version control sorcery.

Push

One more thing that may possibly go wrong is the default Git branch name. Depending on your Git version and configuration, it may be master or main. GitLab has already made the switch to use main as the default, but your local Git is very likely still using master. This is why we did not create the repo through the GitLab website. Do it directly from your command line (don’t forget to change the <username>):

git push --set-upstream git@gitlab.com:<username>/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)

Access

Once everything is uploaded, go to https://gitlab.com/<username>/<repo-name> to find your repository online. You should see pretty much everything that was in your local directory now also uploaded here. Go to the Pipelines under CI/CD from the menu on the left, you should see a list of status reports, with only one item, with a passed mark. If this is the case, you should be able to see the blog at https://<username>.gitlab.io/<repo-name>/ now.

If it says failed, you probably have done something wrong and the blog will not be there. You can try finding some clue from the error logs by clicking on it. If it says running, just wait for it to finish.

By default the blog is only available to view for yourself. You can change this by going to the Settings from the menu on the left, expand Visibility, project features, permissions, and change the option under Pages from Only Project Members to Everyone.

Congratulations! Now you have done it! Yet another blog hosted on GitLab!

Update Content

When you need to edit the existing posts, or some config files, just go to your blog directory and edit the relevant files. When you need to add new posts, do hugo new posts/<new-blog-post-title>.md to generate a new .md file so that you don’t need to manually write the front matter. Once local editing is done, do the usual git stuff to push the changes to GitLab:

git add .
git commit -m 'new post'
git push

You can, of course, follow a proper Git Workflow to organize your writings and updates, instead of simply commit and push everything every time. Either way, within a few seconds after the push, you’ll see your blog updated.

Upgrade Hugo

The Hugo Software

If Hugo was installed from a package manager, upgrades should be included automatically. For example, whenever you do apt upgrade, your apt installed Hugo should be upgraded with everything else (if there is an upgrade).

The Theme

Sometimes later versions of Hugo may include changes that will also affect your theme, or your theme author included some new features. Since it was installed as a Git submodule, just run the following from your project root:

git submodule update --remote --merge

Just List the Commands

Assuming all the prerequisites are satisfied, correct versions of apps (Git and Hugo) are installed, and the blog URL will be https://<username>.gitlab.io/, here is how you can spin up a new blog (mostly) from the command line in minutes. Just replace all occurrence of <username> with your username on gitlab.com.

# create the directory and the files
hugo new site <username>.gitlab.io

# enter it
cd <username>.gitlab.io

# change the default URL
sed -i 's/"http:\/\/example.org\/"/"https:\/\/<username>.gitlab.io\/"/g' config.toml

# use the theme `Diary`
git submodule add https://github.com/AmazingRise/hugo-theme-diary.git themes/diary
echo 'theme = "diary"' >> config.toml

# first post
hugo new posts/hello-world.md

# edit first post
echo "Hello" >> ./content/posts/hello.md
sed -i 's/draft: true/draft: false/g' ./content/posts/hello.md

# GitLab CI
echo "image: registry.gitlab.com/pages/hugo:latest\n\nvariables:\n  GIT_SUBMODULE_STRATEGY: recursive\n\npages:\n  script:\n  - hugo\n  artifacts:\n    paths:\n    - public\n  only:\n  - master" >> .gitlab-ci.yml

# .gitignore
echo "/public" >> .gitignore

# git stuff
git add .
git commit -m "Initial commit"
git push --set-upstream git@gitlab.com:<username>/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)

What Next?

Read Hugo, or the theme documentation for more ideas on how you can customize your site. Or, you can check this list of what I did:

  1. How to put a static file under a specific path of your blog, so than you can configure it to earn some crypto.
  2. How to add commenting function.

  1. I stayed on a locally installed Hugo for more than 2 years until it finally broke. ↩︎


Last modified on 2023-02-14