Deploying using Gitlab CI and SSH

Setting up a Continuous Integration (CI) pipeline can be tricky and hard to debug. Here are some notes I made during my process!

Step 1: SSH Private Key

This step is so that your CI pipeline can connect to and run commands on your server!

If you don’t already have a key-pair, log into your VPS and run ssh-keygen. This will take you through the process of generating one. Assuming you ran the defaults you should be able to find your key-pair at ~/.ssh/.

Head over to your repository on Gitlab and into Settings -> CI / CD Settings. Expand the variables section and create a variable called, for example, SSH_PRIVATE_KEY. The value of this variable is the entire contents of the file ~/.ssh/id_rsa. I tend to cat the file and copy the contents across, but do whatever works best for you.

Step 2: SSH Public Key

This step is so that your server can pull from your Gitlab repository, even if it is private!

cat your ~/.ssh/id_rsa.pub (public key) and paste the contents into the big box at the top of this page over here https://gitlab.com/profile/keys. Don’t forget to give it a descriptive title in case you have multiple.

Step 3: Git Remote

If you are like me, when I cloned my repository onto my server I ended up with HTTPS remotes like this:

$ git remote -v
origin  https://gitlab.com/Finnito/blog (fetch)
origin  https://gitlab.com/Finnito/blog (push)

But this authentication doesn’t use your public key, it tries to use your password. This doesn’t work in CI and you may see an error like this:

fatal: could not read Username for 'https://gitlab.com': No such device or address

To remedy this, simply change the remote of your repository to use SSH like so:

git remote set-url origin git@gitlab.com:Finnito/blog.git
$ git remote -v
origin  git@gitlab.com:Finnito/blog.git (fetch)
origin  git@gitlab.com:Finnito/blog.git (push)

Now, your git operations should use public key authentication and work nice and smoothly in your Gitlab CI.

Step 4: Putting It All Together

Now you are ready to use your SSH_PRIVATE_KEY variable in your .gitlab-ci.yml file to connect to your server, and you are ready for your server to connect to Gitlab back and get set up. Here is my .gitlab-ci.yml as an example.

stages:
  - deploy

variables:
  GIT_SUBMODULE_STRATEGY: recursive

deploy:
  stage: deploy
  before_script:
  - apt-get update -qq
  - apt-get install -qq git
  - 'which ssh-agent || ( apt-get install -qq openssh-client )'
  - eval $(ssh-agent -s)
  - ssh-add <(echo "$SSH_PRIVATE_KEY")
  - mkdir -p ~/.ssh
  - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
  script:
    - ssh finn@my.hidden.ip.address "cd /srv/finn.lesueur.nz/ && git checkout master && git pull origin master --recurse-submodules && exit"
    - ssh finn@my.hidden.ip.address "cd /srv/finn.lesueur.nz/ && git submodule update --remote && exit"
    - ssh finn@my.hidden.ip.address "cd /srv/finn.lesueur.nz/ && rm -rf public/ && exit"
    - ssh finn@my.hidden.ip.address "cd /srv/finn.lesueur.nz/ && hugo && exit"
  only:
    - master
  artifacts:
    paths:
    - public

Good luck!