Using Hugo, GitLab Pages, and Cloudflare to create and run my Blog Site
Tags: webdev, sideprojects, git
Published: 2020-09-10 15:47:29

In this post, I outline how I develop and run my [blog site](https://tkainrad.dev). I briefly explain my technology choices and give detailed instructions about how to use the resulting stack to create such a project. If you stick with me until the end, you will know how to run a beautiful, easily customizable static website for free.

When I decided to start my blog for the reasons outlined in [my first post](../writing-a-swe-blog), I was already familiar with GitLab Pages, and the [MkDocs](https://www.mkdocs.org/) static site generator. I am using this combination for creating documentation websites, and it is working very well.

Therefore, it was an easy choice for me to rely on GitLab Pages also for my blog site. I will cover its features in a [later section](#hosting-with-gitlab-pages) of this post. With hosting already covered, I just needed to create the actual site.

# Creating the site with [Hugo](https://gohugo.io/)

Before starting this blog, the only static site generator I used in production environments was MkDocs, which is intended for documentation and clearly not a good choice for creating a personal blog site. 

A good overview of more appropriate alternatives is given on [staticgen.com](https://www.staticgen.com/). For my purpose, probably all the popular projects would have been good choices. I decided to go with Hugo for two reasons:

1. It is written in Go, a language with a promising future that I wanted to try out for a while already.
2. There is a very well-curated list of open-source themes directly [on the official website](https://themes.gohugo.io/).

So let's get to work. 

To get started on Ubuntu, we can execute just two commands to install hugo and create a new project:

```sh
# install hugo
sudo snap install hugo

# create a new project
hugo new site personal-blog
```

If you are not using Ubuntu, it is likely that your system's package manager provides a similarly easy experience. Refer to [the official documentation](https://gohugo.io/getting-started/installing) for installation instructions for other operating systems.

Before we start to work on our site, we want to install a theme:

```sh
# install the theme
cd personal-blog
git init
git submodule add https://github.com/luizdepra/hugo-coder.git themes/hugo-coder
```

I like that Hugo is very forward about using [GIT submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to install themes. This makes it easy to apply changes to the theme while keeping the connection to the original theme project. In an upcoming blog post, I will detail which changes I made to the theme itself. For now, we are perfectly fine with the default [Hugo-Coder](https://github.com/luizdepra/hugo-coder/) theme. 

We only need to apply some basic configuration. Simply replace the content of `config.toml` with the following:

```yaml
baseurl = "http://www.example.com"
title = "johndoe"

theme = "hugo-coder"

[params]
    author = "John Doe"
    description = "John Doe's personal website"
    keywords = "blog,developer,personal"
    info = "Full Stack Magician"
    avatarurl = "images/avatar.jpg"
    footercontent = "Enter a text here."

[taxonomies]
    series = "series"

# Menu links
[[menu.main]]
    name = "Blog"
    weight = 1
    url  = "/posts/"
```

Then, we create the first post:

```sh
hugo new posts/my-first-post.md
```

For the theme to properly recognize the new blog post, we have to replace the content of the newly created `/content/posts/my-first-post.md` with the following:

```yaml
+++
date = "2019-01-01"
title = "My first Post"
slug = "my-first-post" 
tags = []
categories = []
series = []
+++
```

Finally, we can get a first look at our new personal portfolio and blogging site, by starting up the Hugo development server. Simply type

```sh
hugo server
```

Then go to [http://localhost:1313/](http://localhost:1313/) to see your new site:


![hugo-coder-default-page](https://dev-to-uploads.s3.amazonaws.com/i/hxy6nsikadzfzyh8tkkk.png)


Testing your site locally is great, but showing the whole world that you are *John Doe, the Full Stack Magician* is something else entirely. Luckily, we are very close to doing just that.

# Hosting with [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/)

GitLab Pages is similar to GitHub Pages.
Both allow free hosting of static websites. GitLab Pages enables you to build the hosted static content via GitLab's CI/CD features. This means that your site will automatically be rebuilt on every commit to the repository. Until the release of GitHub Actions, GitHub could not do this. Instead, you had to build the site locally and commit the static files to the repository. Nowadays, both GitLab and GitHub are perfectly fine choices to host your static site.

Naturally, we start by creating a new repository.  Before pushing anything to this repository, you might want to perform some usual GIT maintenance tasks, such as creating a `.gitignore` file. If you do not know it yet, you should have a look at the [gitignore.io service](https://www.gitignore.io/) service. The site generates `.gitignore` files depending on your technology stack. For Hugo, it will for example exclude the `/public/` folder, which is very much what we want, because we will build this folder via a GitLab CI pipeline on every push to the repository.

Once everything is in order, we can simply follow GitLab's instructions for pushing our existing project folder:

```sh
cd personal-blog
git remote add origin git@gitlab.com:<path-to-your-repository>
git add .
git commit -m "Initial commit"
git push -u origin master
```

Finally, we get to the exciting part: Having our site built and published on every new commit to the repository. If you are not yet familiar with GitLab's CI/CD features, check out [their docs](https://docs.gitlab.com/ee/ci/).

Fortunately, getting started is very simple. Simply put a file called `.gitlab-ci.yml` in the root of your project. This YAML configuration file holds all CI/CD configuration. For our Hugo-powered blog, the following file content is sufficient:

```yaml
image: monachus/hugo

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

The first line specifies the docker base image to use for our pipeline. For this brief tutorial, we will not use any testing stages, instead, we have just one simple job called `pages`. It executes the `hugo` command, which will build the static site content in the `public` directory. Somewhat incidentally, this is also where GitLab Pages expects to find our static site content. The only thing left to do is publishing the `public` directory as [a job artifact](https://docs.gitlab.com/ee/user/project/pipelines/job_artifacts.html). The last two lines are optional and state that the site should only be built and published on commits to the `master` branch.

Once you push this file to your repository's master branch, the static site will be built and published within a few minutes. Go to *Settings->Pages* to verify that everything is in order and to see the default URL of your new website. If you own a domain you would like to use rather than the default *.gitlab.io one, you can head [to the docs](https://docs.gitlab.com/ee/user/project/pages/getting_started_part_three.html) or check out this [more detailed tutorial](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). If you want, you can enable [automatic certificate management using Let’s Encrypt](https://docs.gitlab.com/ee/user/project/pages/custom_domains_ssl_tls_certification/lets_encrypt_integration.html) for your custom domain. In case you use Cloudflare as described in the next section, you will not need this though.

# Speeding up load times and configuring URL redirections with [Cloudflare](https://www.cloudflare.com/)

My initial reason for trying out Cloudflare with this setup was that I had problems redirecting from www to non-www URLs with Namecheap, which is my domain name registrar. The issue was complicated by the fact that the site uses a .dev domain which is by default on the [HSTS preload list](https://opensource.google.com/projects/hstspreload). Namecheap seems to prefer selling their own SSL certificates rather than fully support and document working with Let's Encrypt.

I solved this issue by simply signing up to Cloudflare. The free version is perfectly fine for me. After signing up, Cloudflare provides very simple and precise instructions for getting started. Basically, you have to configure your domain to use their nameservers instead of the ones provided by your original domain name registrar. 

Once your site is configured to run with Cloudflare, you can use their *Page Rules* feature to easily redirect traffic from the www. subdomain to your canonical non-www URL or the other way around.

Furthermore, Cloudflare provides SSL encryption out of the box from their servers to your clients. All your static sites hosted with GitLab Pages and served via Cloudflare will be accessible over HTTPS without any additional configuration. Of course, we also want to encrypt the traffic from Cloudflare to the origin GitLab Pages host. For this, Cloudflare offers *Origin Certificates*. I will not explain this further, as there is an excellent [tutorial on this provided directly by GitLab](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/).

It is hard to put a number on the speedup that Cloudflare provides for my site. I can say though that it is able to handle a significant load without any issues. My busiest weekend has been when [my post on personal knowledge management](https://tkainrad.dev/posts/managing-my-personal-knowledge-base/) reached the front page of Hacker News and attracted more than 30k unique viewers in two days. The site didn't flinch.

# Conclusion

The biggest advantage of this setup is its financial cost. Apart from buying the [tkainrad.dev](http://tkainrad.dev) domain, creating my blog site did not cost any money. Furthermore, it is now running at absolutely zero cost. This is not restricted to a limited timeframe, thanks to GitLab Pages. This is much better than having to resort to chemistry puns for reducing our hosting bills:

![xkcd Comic](https://imgs.xkcd.com/comics/reduce_your_payments.png)

If you have basic programming skills, I highly recommend hosting your static sites with this free service instead of paying a monthly fee to a hosting provider.