Hugo website on GitLab Pages

August 6, 2017

How we created a static website with Hugo and hosted it on GitLab Pages with our custom domain secured with a SSL/TLS certificate, for free.

With Hugo + GitLab Pages + Let’s Encrypt you get:

  • Static website generator.
  • Auto-deploy on checkin.
  • Free hosting.
  • Custom domain AND secured with SSL/TLS encryption.
  • Free Let’s Encrypt certificate.

Previously we had [setup our website on GitHub] (/post/hugo-website-on-githubpages/) but with one major drawback: Github Pages doesn’t currently support SSL/TLS certificates with a custom domain. With GitLab Pages, you can use a custom domain AND add your own certificate.

With GitLab Pages your URL options are:

  1. Encrypted sub domain of GitLab Pages, for example:
  2. Unencrypted custom domain, for example: http://mycustomdomain.
  3. Encrypted custom domain, for example: https://mycustomdomain.

We use Let’s Encrypt to generate a 90 day TLS certificate for free. We mostly followed [] ( with a few modifications.

We are running on Linux, your commands might differ. In the following sustitute ‘mywebsite’ with your project name.

Step 1: Add mywebsite project in GitLab

Step 2: Install Hugo, set up your project & push

You might want to look at Hugo - getting-started. Here is how we installed Hugo:

$ sudo apt install snapd
$ snap install hugo
$ export PATH=$PATH:/snap/hugo/current/bin

Download the starter project from [] ( and rename as mywebsite. This project comes complete with the GitLab deployment script. Then:

$  cd mywebsite
$  git init
$  git config --global "Joe Blogs"
$  git config --global ""

# Note: do not add a theme as a git submodule! Just download the theme files and $ git add .

$  git remote add origin
$  git add .
$  git commit -m "Initial commit"
$  git push -u origin master

Wait for a minute then check your website is deployed at your GitLab Page, for example:

The nice thing about GitLab’s deployment script is you only check in the source, not the generated html. No public folder or special deployment branch is required. On push, GitLab’s deployment script runs Hugo on the server to generate and deploy the content for you.

Ensure that you are running the same version of Hugo locally and in the deployment script. For example if you get a 404 page-not-found when you check your deployed website, take a look in GitLab / jobs: even if it might have suceeded, check that the Hugo build logging exactly matches what you see when running $ hugo server -D locally, for example:

$ hugo
Started building sites ...
Built site for language en:
0 draft content
0 future content
0 expired content
36 regular pages created
100 other pages created
0 non-page files copied
56 paginator pages created
0 archives created
42 tags created
2 categories created
total in 494 ms

We fixed the 404 page-not-found error by updating the deployment script hugo version to match our local version, for example:

// .gitlab-ci.yml
  HUGO_VERSION: '0.25.1'
  HUGO_SHA: 'fbf8ca850aaaaad331f5b40bbbe8e797115dab296a8486a53c0561f253ca7b00'

Add some content to your project, commit and push to GitLab.

Step 3: Update your DNS server and GitLab project settings

Log into your domain’s admin control panel and add a DNS A record pointing GitLab’s IP address. For projects on, the IP is

Also add a CNAME record pointing ‘www’ to the same domain ( or IP address )

DNS settings:

Type Host Target TTL

In under the projects' settings/pages link click the big green button to add a domain. For now just add the and leave the ssl certificate fields blank.

Update your hugo configuration:

# .config.toml
baseURL = ""

Git Commit and push

Step 4: Generate SSL/TLS Certificate & upload to GitLab

We are using Let’s Encrypt’s ‘certbot’ to generate the certificate for free. Or you could buy a certificate.

  1. Install and prepare certbot:

    Install certbot:

    $ sudo apt-get update
    $ sudo apt-get install software-properties-common
    $ sudo add-apt-repository ppa:certbot/certbot
    $ sudo apt-get update
    $ sudo apt-get install certbot

    Create a cli.ini file with contents similar to the following:

    # .cli.ini
    text = True
    domains =
    email =
    rsa-key-size = 4096
    logs-dir = /tmp/certbot/logs/
    work-dir = /tmp/certbot/work/

    Update .gitlab-ci.yml:

    # .gitlab-ci.yml
      mkdir -p public/.well-known/acme-challenge/
      cp -R acme-challenge public/.well-known/
  2. Run certbot:

    $ certbot certonly -a manual -c cli.ini

    Part way through running certbot you will need to commit and push the challenge url file as required by certbot to prove your ownership of the domain, for example:

    $ cd acme-challenge
    $ echo s3PmrAqvcx_Zf_7jX7pklYOT9nM9oUKhV1ij2UZR9w4.xN-zdWKk8ATOGjLsHrWTEAOAfvP_TVx4lCT87YNwgyI > s3PmrAqvcx_Zf_7jX7pklYOT9nM9oUKhV1ij2UZR9w4

    Git commit and push

    Test that the challenge url works, for example:

    Continue running certbot, noting the location of the certificates when it completes:

    Congratulations! Your certificate and chain have been saved at
    /etc/letsencrypt/live/ Your cert will
    expire on 2017-11-04. To obtain a new or tweaked version of this
    certificate in the future, simply run certbot again. To
    non-interactively renew *all* of your certificates, run "certbot
  3. Add the certificate to GitLab

    Update your hugo configuration:

    # config.toml
    baseURL = ""

    Git commit and push

    Go back to under the projects' settings/pages link. Delete the domain you added earlier. Click the big green button to add a domain. Add ‘’ and paste in the fullchain.pem and privkey.pem contents into the certificate fields.

    Add another domain for ‘’ and add the same certificate details.

  4. Enforce SSL

    Update your hugo configuration:

    # config.toml
      enforceSSL = true

    Add this to a layout or common partial:

    // layouts/partials/nav.html
    {{ if .Site.Params.EnforceSSL }}
      <script type="text/javascript">
      if (window.location.protocol != "https:")
          window.location.protocol = "https";
    {{ end }}

    Git commit and push

Your custom domain should now be secured at and

Finally, make a note in your diary to renew the certificate 3 months later, by following:

From now on you can add further content to your project, commit and push to GitLab, and see your website updated accordingly.

November 2017 update: Certificate Renewal!

Unfortunately with using the manual verification step, it was not that simple to renew and we had to re run some of the steps above. The challenge page was different so we had to commit and push that again during the certbot execution. Here in summary are the steps we took to renew (for more details see above):

  1. Set enforceSSL = false, commit & push.
  2. ( you may or may not need to repeat the cerbot install steps ).
  3. Run $ ./certbot-auto certonly -a manual -c cli.ini
  4. Pause and add the acme-challenge file, commit and push, wait for GitLab to deploy.
  5. Continue to finish.
  6. Delete & in GitLab Pages, add them again with certificates.
  7. set enforceSSL = true, commit & push.

You are done for another 3 months. Note that it may take a little while for the new certificate to show up in the browser.

© 2020 Keith P | Follow on Twitter | Git