Continuous Deploy Of Gopher Site

A few months back I built a tool to cross-compile my Hugo blog into one that can be accessed via the Gopher protocol.

More recently, I built a CI/CD pipeline on my GitLab* instance which automatically compiled and uploaded my website to AWS. I won’t cover that here, but I found a wonderful and perfect guide for it.

Sometime yesterday I decided to take this all one step further and integrate my hugogopher into that CI/CD pipeline. This means every time I commit a new post, not only does the blog get uploaded to the world wide web, but another version of it is compiled to a Gopher site and uploaded to the server that I have running.


You’ll need a .gitlab-ci.yml file already in your Hugo project, this should be configured to upload your compiled HTML site to your hosting provider of choice. Here’s a redacted version of mine as an example:

  - build_html
  - deploy_html

  AWS_DEFAULT_REGION: ap-southeast-2

  image: monachus/hugo
  stage: build_html
    - hugo
      - public
    - master

  image: garland/aws-cli-docker
  stage: deploy_html
    - buildHugoSite
    - aws configure set preview.cloudfront true
    - aws s3 sync ./public s3://$BUCKET_NAME --delete;
    - aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_DIST_ID  --paths "/*";
    - master

Create an account on your Gopher server that the GitLab Runner can access to upload the gopher files. Make sure it has the right groups to modify the directory where your Gopher files are stored. Also make sure that the Gopher files have permissions set recursively to allow writing by the group (traditionally 775 permissions on a POSIX system).

You’ll need to generate an SSH key and store the public key in the ~/.ssh/authorized_keys file on your Gopher server for the GitLab user account.

Compiling Gopher

Add build_gopher as a stage to .gitlab-ci.yml, then append the following:

  image: node:lts-alpine
  stage: build_gopher
    - apk update && apk upgrade && apk add --no-cache bash git
    - git clone
    - cd hugogopher
    - node index.js '../content'
      - hugogopher/gopher
    - master

This stage will do the following:

  1. Use a tiny Alpine Linux image with Node.js.
  2. Install bash and git.
  3. Clone the hugogopher repo.
  4. Compile the project as a Gopher site.
  5. Store the compiled files as an artifact.

Uploading Gopher

Copy the contents of the private key you have generated, then go to your project repository on your GitLab instance and click Settings -> CI/CD, then open the Variables section. Create a new variable and call it SSH_PRIVATE_KEY, and set the value to be the contents of the private key. Save the updated variables.

Add a deploy_gopher stage to your .gitlab-ci.yml, then append the following:

  image: alpine:latest
  stage: deploy_gopher
    - buildGopherSite
    - apk update && apk upgrade && apk add --no-cache bash openssh-client
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - scp -o StrictHostKeyChecking=no -r ./hugogopher/gopher

This stage does the following:

  1. Relies on the previous build_gopher stage (and it’s artifacts).
  2. Uses a minimal Alpine Linux image
  3. Installed bash and an SSH client
  4. Uses ssh-agent to add and manage the private key
  5. Uses scp to transfer the artifact files to the Gopher server

Finishing Off

Commit the updated .gitlab-ci.yml file in your Hugo project and upload it to GitLab and very soon you should have your Gopher site uploaded and running!

If you want, you can check mine out at gopher:// or

* Buy my book on GitLab! Available on Packt or on Amazon.