Deploying Hugo with Gitlab CI

Building and deploying Hugo is really easy. You just type hugo in the console, and all you need is in public/. Just copy and paste this on your server, and you’re all done. So… why complicate thing with the CI?

Because why not? It’s easy to automate, and it saves you from manually using rsync or scp. And the fun thing about this, the GitLab’s CI is easy to use, and totally free.

Setup Hugo project

For the sake of this tutorial I will assume that you already have a Hugo web site. However, for those who don’t have it, and are maybe considering it, this part will explain, in short, how to start with Hugo.

First, make the directory for your project, and install hugo using snap (or consult documentation for your preferred OS package manager):

mkdir -p ~/repos/examples/hugo-ci
cd ~/repos/examples/hugo-ci
snap install hugo

Then, create a new site, bring it under git source control, and add a theme (you can choose a theme here):

hugo new site .
git init
git submodule add themes/hugo.386
echo 'theme = "hugo.386"' >> config.toml
echo 'public/' >> .gitignore

And finally add a blog post:

hugo new posts/

You can preview your blog, by typing hugo server in your console. Finally, commit changes before continuing to the next step, and push this to your GitLab.

git add -A
git commit -m "Add hugo"
git push origin master

Building the Pipeline

GitLab gives you the ability to use CI/CD pipeline for free (although build minutes are limited on a monthly level). All you have to do is to include .gitlab-ci.yml at the root of your project, and start using the pipeline.

We will split out pipeline into two stages, build and deploy. Add this to your .gitlab-ci.yml:

  - build
  - deploy

Our first task will be to build the static web site:

  stage: build
    - hugo
      - public

hugo command will build the site, and place it automatically in the public/ directory. We are going to cache this directory (save it as an artifact) and use it in the next stage for deployment.

If you are following along this tutorial, go ahead and sign up for DigitalOcean (this is affiliate link), and spin up one instance for your hugo blog. In order to sync files between pipeline and your droplet, you’ll need to create SSH key pair.

ssh-keygen -t rsa -f ~/.ssh/

Copy your public key to ~/.ssh/authorized_keys on your instance. You can view the contents of your public key with:

cat ~/.ssh/

Now, add your private key as an environment variable to the GitLab. For this, we will base64 encode the key, and paste it as a variable in GitLab:

cat ~/.ssh/ | base64

Go to your project in Gitlab, and go to Settings -> CI/CD -> Variables, click Expand, and then click the Add Variable button. Paste the key in SSH_PRIVATE_KEY variable:


Add environment variable context

Add following variables too:

Env Var Value Explanation
DO_USER root Droplet user
DO_HOST Paste your actual droplet IP
DO_PATH /var/www/blog Path to your website

Now, finally, you can define deploy task in your pipeline:

  stage: deploy
  image: alpine:latest
    - apk add --upadte --no-cache rsync
    - mkdir -p ~/.ssh && chmod 700 ~/.ssh
    - echo ${SSH_PRIVATE_KEY} | base64 -d > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -t rsa ${DO_HOST} >> ~/.ssh/known_hosts
    - rsync -az --delete public/ ${DO_USER}@${DO_HOST}:${DO_PATH}
    - master

For this task, we’re using lightweight Alpine docker image. We start by installing rsync, and adding our private key to the container. Finally, we use the rsync to copy our files to the droplet.

And that’s it! It is easy as that to automate your Hugo deployment. You can also schedule your pipeline with GitLab. This is useful because you won’t need to manually trigger your builds when the time comes to deploy your new post. You can find the full example on this gist.

We're not spammers, and you can opt-out at any moment. We hate spam as much as you do.

powered by TinyLetter

See also