Automatically Deploy a Hugo Site with GitLab CI/CD

Automatically Deploy a Hugo Site with GitLab CI/CD #

I wanted publishing a change to this site to be more than a reminder to log in to the server and run Hugo by hand. My GitLab pipeline connects to my Hugo server and builds the site there when I push to master.

This setup uses a self-hosted GitLab Runner that can reach the Hugo server on my home network. The pipeline does not upload a build artifact: the server has its own checkout of the blog repository, and the deploy job pulls the latest changes before running Hugo.

The pipeline #

Here is the relevant .gitlab-ci.yml:

stages:
  - deploy

deploy:
  stage: deploy
  rules:
    - if: '$CI_COMMIT_BRANCH == "master"'
  script:
    - eval $(ssh-agent -s)
    - echo "$HUGOCI_SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - ssh-keyscan -H hugo.home.arpa >> ~/.ssh/known_hosts
    - ssh hugoci@hugo.home.arpa "cd /var/lib/snapd/void/blog && git pull && /usr/local/bin/hugo"

What happens when I push #

When I push to master, GitLab runs the deploy job:

  1. Branch rule: The job is limited to the master branch by its rules.
  2. Authenticate: The job starts an SSH agent and loads the private key stored in the HUGOCI_SSH_PRIVATE_KEY CI/CD variable. Removing carriage returns helps avoid problems if the key value has Windows-style line endings.
  3. Connect and build: The job SSHes to hugo.home.arpa as hugoci. On the server, it changes to the site’s Git checkout, pulls its tracked changes, and runs Hugo from /usr/local/bin/hugo.

The build runs on the Hugo server, so the runner does not need to install Hugo or transfer generated files. The server does need a working Git checkout of the blog repository and a usable Hugo installation at the path in the command.

Setting up the prerequisites #

Before using a pipeline like this, make sure:

  • A GitLab Runner is registered for the project and can reach the Hugo server over SSH.
  • The runner can resolve and connect to hugo.home.arpa over SSH. Because this is a private hostname, the runner needs access to the network where the server lives.
  • The hugoci account on the server has the public key corresponding to the CI private key in its authorized_keys.
  • The server’s checkout at /var/lib/snapd/void/blog has the correct Git remote and branch tracking configuration. git pull updates the branch that checkout is configured to track.
  • Hugo is installed on the server at /usr/local/bin/hugo.
  • HUGOCI_SSH_PRIVATE_KEY is configured in the project’s GitLab CI/CD variables. It contains the private key itself, since the script passes the variable contents to ssh-add.

Keep the private key in GitLab’s CI/CD variables, not in the repository. Since the deploy job only runs on master, make sure the variable is available to that branch if it is configured as a protected variable.

Create the deployment account and SSH key #

Create a dedicated account on the Hugo server. Run these commands there as a user with sudo privileges:

sudo useradd -m -s /bin/bash hugoci
sudo mkdir -p /home/hugoci/.ssh
sudo chmod 700 /home/hugoci/.ssh

Generate a dedicated key pair on a trusted machine, not on the Hugo server. The private key will be stored in GitLab, and the public key will authorize the hugoci account:

ssh-keygen -t ed25519 -C "gitlab-hugo-deploy" -f "$HOME/.ssh/hugoci_deploy_key" -N ""

This creates ~/.ssh/hugoci_deploy_key (private) and ~/.ssh/hugoci_deploy_key.pub (public). The empty passphrase is needed for this non-interactive CI job; protect the private key by keeping it out of the repository and limiting access to the GitLab variable.

Display the public key:

cat "$HOME/.ssh/hugoci_deploy_key.pub"

As an administrator on the Hugo server, append the complete output as a single line in /home/hugoci/.ssh/authorized_keys. For example, open the file with:

sudo vim /home/hugoci/.ssh/authorized_keys

Then set ownership and permissions on the server:

sudo chown -R hugoci:hugoci /home/hugoci/.ssh
sudo chmod 700 /home/hugoci/.ssh
sudo chmod 600 /home/hugoci/.ssh/authorized_keys

Add the private key to GitLab #

In the GitLab project, open Settings > CI/CD > Variables, select Add variable, and create a variable with:

  • Key: HUGOCI_SSH_PRIVATE_KEY
  • Type: Variable (not File), because the pipeline passes the variable contents to ssh-add
  • Value: the complete contents of ~/.ssh/hugoci_deploy_key, including the BEGIN and END lines
  • Protected: enable this if master is a protected branch; a protected variable is only made available to pipelines on protected refs

To display the private key so it can be copied into the value field:

cat "$HOME/.ssh/hugoci_deploy_key"

Copy the private key you created earlier (~/.ssh/hugoci_deploy_key) and paste its complete contents into the Value field for HUGOCI_SSH_PRIVATE_KEY.

GitLab project CI/CD variable for the Hugo deploy key