Today, there are three different options for building Docker images in GitLab CI, and each of them comes with security and complexity tradeoffs. Depot fixes a lot of these tradeoffs, and can be used to build images in GitLab CI quickly and securely.
There are three different options for building images in GitLab CI/CD jobs:
- You can use the
shellexecutor on your own GitLab Runners. This requires configuring the Docker Engine and granting the
gitlab-runneruser full root permissions to invoke
- You can use Docker in Docker (
dind) with registered runners. This uses the
dindimage provided by Docker, so it contains all the
dockertools. To build new containers with
dind, you have to run the runner container in privileged mode.
- Or, you can bind to the Docker socket by bind mounting
/var/run/docker.sockinto the container where your build is running.
All these options come with tradeoffs and downsides.
The first option requires you to manually configure your own runners and grant them root permissions. The second option requires that each build job get its own instance of the Docker engine, so jobs are slower because there is no layer caching, and privileged mode is still required. The third option requires you to bind mount the Docker socket into your container, which exposes the underlying host and any other processes on that host to privilege escalation.
With Depot, you can build your Docker images from GitLab CI/CD jobs without needing to configure
shell executor runners, work around slow builds with
dind, or bind mount the Docker socket. Instead, the
depot CLI builds the container using Depot's remote builders, and can optionally push the resulting image to a registry.
The Depot CLI can be installed via a
before_script: - curl https://depot.dev/install-cli.sh | DEPOT_INSTALL_DIR=/usr/local/bin sh
After the CLI is installed, you can build your image with the
depot build command:
script: - depot build -t org/image:tag .
Most likely, you will want to push your image to a registry, with the
depot build --push flag. Depot uses the local Docker registry credentials when pushing, so you will need to configure those credentials using one of two options: using the
docker CLI or creating the configuration manually.
The easiest way to configure registry authentication is to use the
docker login command. This will create a
~/.docker/config.json file that Depot will use to authenticate with your registry. Note that this requires running the Docker daemon with Docker-in-Docker (
dind) to perform the login. However, you do not need to run the GitLab CI runner in privileged mode, the daemon is only used for registry login:
image: docker:20.10.16 services: - docker:20.10.16-dind variables: DOCKER_HOST: tcp://docker:2376 DOCKER_TLS_CERTDIR: '/certs' build-job: before_script: - apk add --no-cache curl # install curl as it is not available in the docker:20.10.16 image - curl https://depot.dev/install-cli.sh | DEPOT_INSTALL_DIR=/usr/local/bin sh script: - docker login registry.gitlab.com --username your-username --password $REGISTRY_TOKEN - depot build -t registry.gitlab.com/your-username/myimage:$CI_COMMIT_SHA . --push variables: DEPOT_TOKEN: $DEPOT_TOKEN
With this workflow, you can build native multi-platform images directly from GitLab CI and push them to a private registry. The
depot CLI is installed via
curl and the
dind service is configured with TLS enabled. Giving you the ability to run
docker login to authenticate to a private registry, but leave the build part to Depot.
The Docker-in-Docker service is only used for authentication and not for the actual build. This means you get significantly faster builds because Depot manages the caching of layers for you, unlike
dind which has no layer caching when you use
If you already have a Docker
config.json file with your registry credentials, you can skip the
docker login step and provide the config contents directly via a
DOCKER_AUTH_CONFIG environment variable. Your workflow can save the contents of that variable to
$HOME/.docker/config.json, and Depot will read from that file when pushing. This removes the need for the
docker CLI and
dind service at all in your image build:
build-image: before_script: - curl https://depot.dev/install-cli.sh | DEPOT_INSTALL_DIR=/usr/local/bin sh - mkdir -p $HOME/.docker - echo $DOCKER_AUTH_CONFIG > $HOME/.docker/config.json script: - depot build -t registry.gitlab.com/repo/image:tag . --push variables: DEPOT_TOKEN: $DEPOT_TOKEN
depot build runs, contents of the
DOCKER_AUTH_CONFIG environment variable are saved as
You may be able to generate a
config.json file locally by running
docker login and then copying the contents of the
$HOME/.docker/config.json file and saving it as
DOCKER_AUTH_CONFIG, if you have static authentication credentials for your registry.
There are multiple options for building Docker images in GitLab CI, each with security and complexity tradeoffs. They are not impossible to work around, but they are inconvenient and cause a lot of friction. This friction can manifest as slow builds, security concerns, or having to maintain complex build infrastructure. It's totally unnecessary.
Instead, you can use Depot to build Docker images in GitLab CI quickly and securely, using Depot's remote builders, without needing to configure
shell executor runners, work around slow builds with
dind, or bind mount the Docker socket, and all without having to grant root access to your CI runners.
If your interested in trying out Depot, we offer a 14-day free trial with no credit card required.