Why `services: docker:dind` is needed while already having `image: docker`?

Hello everybody,

If I want to use the docker commands in a job (for example for building an image or pushing to a registry), I understood that within the image: part of the job I have to have an image in which the docker command is available… So let’s simply have:

  image: docker:latest

But then, why I need to have for that job also the following service config?

  image: docker:latest
    - docker:dind

I was thinking that since the job will be ran inside the docker image and that the docker command will be available, why the service is still required?

Also, I tried for example to use docker:latest as the service. That doesn’t work.
While using docker:dind as the image does work.

I’ll be happy to understand the differences.
Thank you very much.

The docker:dind image is required because it actually starts up the docker daemon when it is brought up by a runner.

The docker cli can exist on a host/container without having the daemon running and can be pointed to control a daemon running somewhere else.

I believe you could technically use the docker:dind image as your runner itself, but I imagine docker:dind is more stripped down and due to the way containers are started by gitlab runner (they do not use the entry point specified in your Dockerfile), it is basically only used to start up a docker daemon.

I assume docker:latest May have more tools that you can utilize during the actual build.

1 Like

Thanks for the details…

When I use docker:latest as the service, I got errors. only docker:dind work.
In the meantime I tried to deploy a runner in my own machine. And indeed, in this case I can rid of the services: -docker:dind part and it’s still work (but only if I add "/var/run/docker.sock:/var/run/docker.sock" in the volumes of the runner).

Not sure if this is the best way to do it but I guess I’m understanding a little bit how it works.

Sorry for a late reply. I actually would highly recommend against mounting the docker socket as a volume into your runner image. When you include that as a volume, all runner images on your host now have access to the host’s docker daemon. This could be very dangerous as your builds now have access to delete images on your host at will, and even stop other running containers/builds.

I might recommend using docker:dind as your actual runner image. It’s possible you may have to build your own image based off of that image which will install all the tools you may want/need for your builds.

I recently wrote a blog post of GitLab CI + Docker (using DinD or socket mounts). There are plenty of diagrams there that can help you understand the different configurations:

Note that whether you use the Docker socket or the DinD approach, both are by default not secure (the job can easily gain control of the host machine where the GitLab runner is executing). The blog post offers a solution to this.

Hope that helps!