Gitlab Private Pages + Running Docker containers in Gitlab CI/CD Pipelines

Private pages on Gitlab

Did you know that Gitlab Pages supports private / auth-based static sites? Basically, you add people you want to share your site with as Project Members to the repository. Gitlab then enforces Gitlab Login on the website, so you can only see the content if you are logged into Gitlab (and have been given access to the repository).

This is a very cool way to host private static sites freely (vs, for example, self-hosting + HTTP Basic Auth).

Moving from Github Actions to Gitlab CI/CD Pipelines (+ running Docker containers in CI/CD scripts)

It's ridiculously hard to Google for the right way to run a dockerized tool in CI/CD environments.

Github Actions makes this a nobrainer because their images (example: ubuntu-latest) have docker clients and servers baked in already. So you just write your command (say docker run hello-world) directly into your YAML file and you are done.

Gitlab CI/CD does not have this. They expect you to search + use images from Dockerhub / Gitlab Container Registry. To make matters worse, it isn't enough to use the official Docker in Docker image (which is docker:latest btw). Using this image only gives you the docker client. You still need to run the docker server separately. If you don't, your pipeline will fail with the following error:

docker: error during connect: Post "http://docker:2375/v1.24/containers/create": dial tcp: lookup docker on 169.254.169.254:53: no such host.

To run the server, you need to understand the Services concept of Gitlab CI/CD. In short, this is a way to run services that your main job might need to access when it runs. You can use it, for example, to run database instances that your test job would connect to. In our case, we need a docker daemon service, which is provided by the docker:dind image.

Once you have this, then your entire pipeline will finally run. It took me a frustratingly long time to figure it all out. Here is what my final .gitlab-ci.yml file looks like:

workflow:
  rules:
    - if: $CI_COMMIT_BRANCH

before_script:
  - mkdir -p .neuron/output && touch .neuron/output/.nojekyll

pages:
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker run -v $PWD/content:/notes sridca/neuron neuron gen --pretty-urls
    - cp -R content/.neuron/output public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "master"

The pages job-name is special. Gitlab understands that it is meant for hosting, and expects HTML / assets in a top-level directory called public.

I hope this helps someone else waste less time.

Published On: Tue, 23 Aug 2022.