Skip to content

How to set up Docker in Docker

Some projects create their own Docker containers, stored in the GitLab container registry which they use for build and test. This speeds up pipelines, because each pipeline job no longer needs to install its own OS packages.

Modifying .gitlab-ci.yml

Many Beautiful Canoe repositories follow the recipe here. First, we create a new directory .meta at the top-level of the repository, and place a Dockerfile in that directory.

Next, add a new stage to the pipeline:

stages:
    - prepare
    - ...

Then add a new job to the pipeline to build the Docker container and upload it to the GitLab container registry:

meta-build-image:
    stage: prepare
    image: docker:stable
    variables:
        DOCKER_TLS_CERTDIR: ""
    services:
        - docker:dind
    script:
        - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
        - cd .meta
        - docker build -t $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/CONTAINERNAME:latest .
        - docker push $CI_REGISTRY/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/CONTAINERNAME:latest
    only:
        changes:
            - .meta/Dockerfile
    tags:
        - dind

Note that we only build the Docker container when the Dockerfile has changed, and the container we push is tagged as the latest container no matter which branch the container was built on. You may wish to tweak this, for example to only build the Dockerfile on the develop branch:

    only:
        develop
        changes:
            - .meta/Dockerfile

Lastly, change the other CI jobs to use the new container:

    ...
    image: registry.gitlab.com/$CI_PROJECT_NAMESPACE/$CI_PROJECT_NAME/CONTAINERNAME
    ...

Modify the gitlab-runner configuration

Due to the way Docker supports TLS, if you are running the prepare CI job on Beautiful Canoe servers, you will need to tweak the config.toml for your runner. Firstly, create a new runner, tagged with dind that will only be used for building container images in your repository. Next, locate the config.toml file for that runner. It is likely to be either in /etc/gitlab-runner/config.toml if you are running gitlab-runner as root, or /home/gitlab-runner/.gitlab-runner/config.toml if you are running the service as gitlab-runner.

Edit the file, and add:

image = "docker:dind"
privileged = true
pull_policy = "if-not-present"

to the runners.docker configuration. The first two lines there fix the TLS support issues (by disabling TLS support). The last line pull_policy ... ensures that Docker will use a docker:dine image held locally on the server, if there is one.

Your runner should look something like this:

[[runners]]
  name = "RUNNER-NAME"
  url = "https://gitlab.com/"
  token = "TOKEN"
  executor = "docker"
  [runners.custom_build_dir]
  [runners.docker]
    tls_verify = false
    image = "docker:dind"
    privileged = true
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    pull_policy = "if-not-present"
    disable_cache = false
    volumes = ["/cache"]
    shm_size = 0
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

Further reading