Deploying a Blogdown Website with GitLab Pages


GitLab Pages is a feature that allows you to automate the process of building a static website and publishing it on a server, directly from a repository in GitLab. This is accomplished through a tool called GitLab CI/CD. When the procecss is completed, the website can be accessed through a domain offered by GitLab; optionally, you can add a custom domain and enable the HTTPS protocol.

I have just switched to GitLab Pages to host my blog, which is built with the blogdown package. In this post, I am going to share with you the details of how it is done.

Update: Automated Let’s Encrypt certificates for Gitlab Pages is now available. Some HTTPS settings introduced in this post may no longer be necessary. I’ll update this post later.


Before proceeding, you should know how to: create a git repository to track the source files of your website; create a corresponding project in GitLab and push commits to it. Instructions on how to accomplish these steps are out of the scope of this post. I refer readers in need to some useful documentations (e.g., this and this).

Setting up GitLab CI/CD

To set up GitLab CI/CD, we need a yaml file called .gitlab-ci.yml, which should be placed in the root directory of the project that holds your website’s source files. This file specifies the commands to be executed in a Docker container on GitLab to build your website. Here is what is inside the .gitlab-ci.yml file that I use to build my blog:

image: rocker/tidyverse:3.5.2

  - apt-get update && apt-get -y install pandoc pandoc-citeproc
  - R -e "install.packages('blogdown')"
  - R -e "blogdown::install_hugo(version = '0.44')"

    - R -e "blogdown::build_site()"
      - public
    - master

Now, let me explain this file in more details.

The first line of this file specifies a Docker image for the container to use. Here the image is rocker/tidyverse:latest, which is built on debian and includes the latest version of r-base and tidyverse packages1. There are other images (see here) like rocker/r-base that only includes base packages of R; however, using rocker/tidyverse has an advantage of accelerating the build process. This is because it eliminates the need to compile many packages required by the blogdown package.

Then, under the before_script section, I tell the container to install the packages which will be used to generate the website, including pandoc, pandoc-citeproc, blogdown and hugo.

Finally, the build process will be completed by a job called pages. In this section, the command R -e "blogdown::build_site()" is executed and by default the generated website files will be stored in a directory named public/. The artifacts subsection indicates where to find the files, which will be uploaded to the GitLab Pages server. Additionally, in the only subsection, I tell the container only to build the website in the master branch.

The official documentation of GitLab offers more details on how to tweak the .gitlab-ci.yml file.

After the above configuration, when I push commits to GitLab, my website will be automatically built. If the build is successful, then I can access my blog via https://<username> if it is a user page; or https://<username><projectname>/ if it is a project page (what is the difference?).

Note that by default, a static site generator (e.g., Hugo) expects to find your website under a domain (e.g., https://<username>, not in a subdirectory of that domain (e.g., https://<username><projectname>). Therefore, whenever you want to publish a project page, you’ll first have to configure the baseurl to reflect this pattern. To do this for Hugo (which is the static site generator that powers blogdown), you can set the parameter baseurl in the config.toml file to https://<username><projectname>/.

That is it. If you don’t need a custom domain for your website, then the above configuration is all you need to get the site published on GitLab Pages server. However, if you do want to use a custom domain for your website, then please continue reading.

Adding a custom domain and enabling HTTPS protocol

Note that the following content requires some knowledge on domain name, DNS web service and the HTTPS protocol. If you are confused, you may find this documentation to be useful.

Cloudflare is a network platform that provides free DNS service and SSL/TLS Certificates. The following instructions on how to add a custom domain to a GitLab Pages and enable HTTPS are based on cloudflare.

First, login to cloudflare, go to the control panel of your domain, and:

  • Navigate to the DNS tab and create a new CNAME record pointing a custom domain (e.g., to the GitLab Pages domain (e.g., (Fig. 1).

Figure 1

  • Navigate to the Crypto tab; then, in the Origin Certificates section, click the Create Certificate button and generate an RSA private key and a CSR (Fig. 2); copy the generated certificate and private key for use in GitLab.

Figure 2

  • In the same section, make sure that SSL mode is Full (strict), and that Always Use HTTPS is On (Fig. 3-4).

Figure 3

Figure 4

Then, login to GitLab, and:

  • Go to Settings > Pages from your project’s dashboard and click the New Domain button (Fig. 5); add the custom domain, the certificate2 and private key and click Create New Domain.
  • Check the verification status of the custom domain and copy the verification code for use in the following step.

Figure 5

Go back to cloudflare, and:

  • Navigate to the DNS tab and create a new TXT record for the custom domain, with the verification code (Fig. 1).

Finally, go back to GitLab, and:

  • Verify the ownership of the custom domain.
  • Make sure that Force domains with SSL certificates to use HTTPS is checked (Fig. 5).

Note that DNS propagation needs some time to take effect. So don’t worry if you can’t access your website via your custom domain instantaneously. Just wait for some time and check it again.

Additional notes

  • Hugo has not reached a very stable release yet. Therefore, it is recommended to install a specific version of Hugo (e.g., R -e "blogdown::install_hugo(version = '0.44')") to build a website in order to avoid breaking changes.
  • When using GitLab CI/CD to build the website, you need to make sure all the content generated by code can be reproduced on the server; otherwise, the build process won’t succeed. To reduce complexity in maintenance, keep executable code in .Rmd files as few as possible.
  • Remember to configure the settings so that requests always uses the HTTPS protocol to avoid redirect loop.
  • To publish a plain website without a static site generator (e.g., Hugo, blogdown), just move the content to a directory named public/ and specify the path to the artifacts in .gitlab-ci.yml. Here is an example:
  stage: deploy
  - mkdir .public
  - cp -r * .public
  - mv .public public
    - public
  - master

  1. If reproduciblity is paramount, you can use the version tag, e.g., rocker/tidyverse:3.5.2.

  2. Note that since cloudflare doesn’t combine both PEM and root certificates in one, so we need to copy the root certificate (aka “intermediate”) from here and paste it below the certificate.


comments powered by Disqus