Automate Hugo deployment with GitLab CI/CD
To make it easier to write new posts I wanted to automate the deployment of updates to my Hugo website.
The goal is whenever a new commit is added to the master branch, the site is rebuilt using Hugo and deployed to my server using rsync.
Quick note: If your site is entirely static, Netlify (I’m in no way affiliated with them) will do this for you along with lots of extra features like generating previews for pull requests. I use it to generate the sceditor.com website where it works well.
Prerequisites
You will need to set up a user and/or key for rsync to connect to your server with.
The Hugo website has instructions for setting up an SSH key if you don’t already have one set up.
GitLab setup
Private key
The private key needs to be added as a variable to the repository so that GitLab CI can access it. From the repository go to:
Settings -> CI/CD -> Variables
Click the add variable and then add the key giving it the name
RSYNC_PRIVATE_KEY
:
Known hosts
Every time SSH connects, it checks the key from the server against ones it has seen before and warns if it hasn’t seen the key before.
Each time GitLab CI/CD is run it creates a new VM instance, so any previously
seen host keys are not saved. It’s possible to just always accept new keys but
that could open up MITM attacks. To avoid that, it’s best to add the keys from
the server to the known_hosts
file.
To set up the known_hosts
file, add a new variable called RSYNC_KNOWN_HOSTS
with the contents of the following command:
ssh-keyscan -H your.hostname.com
CI/CD setup
Finally, add a file called .gitlab-ci.yml
with the following contents:
# Use GitLabs built-in Hugo image
# Might be wise to change from latest to a specific version of Hugo
# to ensure nothing breaks when new versions of Hugo are released.
#
# For example:
# image: registry.gitlab.com/pages/hugo:0.81.0
image: registry.gitlab.com/pages/hugo:latest
variables:
GIT_SUBMODULE_STRATEGY: recursive
deploy:
# Set sane timeout, for me it takes ~25 seconds to run so
# 5 minutes is reasonable
timeout: 5 minutes
script:
# Need SSH client for rsync to use
- apk add rsync openssh-client
# Add the key and known hosts
- mkdir -p ~/.ssh
- echo "$RSYNC_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- echo "$RSYNC_PRIVATE_KEY" > ~/.ssh/key
- chmod -R 700 ~/.ssh
- eval "$(ssh-agent -s)"
- ssh-add ~/.ssh/key
# Build the website
- hugo
# Sync the built site
# IMPORTANT: Make sure to modify the rsync command below
# for your sever. Remove --dry-run when ready to deploy
- rsync -crtvz --dry-run --delete ./public/ user@host:/path/to/public_html/
only: # Only run on main branch
variables:
- $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
Change the rsync command parameters to match your server.
I’ve added the --dry-run
option set so should be safe to run but it would be
wise to backup your server before committing it to the repository.
Check the output and once happy, remove the --dry-run
option.
Done!
That’s it, now whenever a new commit is made to the master branch it will be automatically built and deployed.
GitLab offers free users 400 minutes per month to use which should be plenty for most people (takes around ~25 seconds to run for me). If you’re going to be doing a lot of deploys per day, or if you have a lot of websites, GitHub offers 2000 minutes per month for free users which might be a better choice.
Comments