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.
config.toml
.gitlab-ci.yml
.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:
- How to put a static file under a specific path of your blog, so than you can configure it to earn some crypto.
- How to add commenting function.
-
I stayed on a locally installed Hugo for more than 2 years until it finally broke. ↩︎
Last modified on 2023-02-14