Using Docker images
Docker is an open-source project that allows you to use predefined images to run applications in independent "containers" that are run within a single Linux instance. Docker Hub has a rich database of pre-built images that can be used to test and build your applications.
Docker, when used with GitLab CI, runs each job in a separate and isolated container using the predefined image that is set up in
This makes it easier to have a simple and reproducible build environment that can also run on your workstation. The added benefit is that you can test all the commands that we will explore later from your shell, rather than having to test them on a dedicated CI server.
Register Docker Runner
To use GitLab Runner with Docker you need to register a new Runner to use the
A one-line example can be seen below:
sudo gitlab-runner register \ --url "https://gitlab.example.com/" \ --registration-token "PROJECT_REGISTRATION_TOKEN" \ --description "docker-ruby-2.1" \ --executor "docker" \ --docker-image ruby:2.1 \ --docker-postgres latest \ --docker-mysql latest
The registered runner will use the
ruby:2.1 Docker image and will run two services,
mysql:latest, both of which will be accessible during the build process.
What is an image
image keyword is the name of the Docker image the Docker executor will run to perform the CI tasks.
For more information about images and Docker Hub please read the Docker Fundamentals documentation.
What is a service
services keyword defines just another Docker image that is run during your job and is linked to the Docker image that the
image keyword defines. This allows you to access the service image during build time.
The service image can run any application, but the most common use case is to run a database container, e.g.,
mysql. It's easier and faster to use an existing image and run it as an additional container than install
mysql every time the project is built.
You are not limited to have only database services. You can add as many services you need to
.gitlab-ci.yml or manually modify
config.toml. Any image found at Docker Hub or your private Container Registry can be used as a service.
Services inherit the same DNS servers, search domains, and additional hosts as the CI container itself.
You can see some widely used services examples in the relevant documentation of CI services examples.
How services are linked to the job
To better understand how the container linking works, read Linking containers together.
To summarize, if you add
mysql as service to your application, the image will then be used to create a container that is linked to the job container.
The service container for MySQL will be accessible under the hostname
mysql. So, in order to access your database service you have to connect to the host named
mysql instead of a socket or
localhost. Read more in accessing the services.
How the health check of services works
Services are designed to provide additional functionality which is network accessible. It may be a database like MySQL, or Redis, and even
docker:stable-dind which allows you to use Docker in Docker. It can be practically anything that is required for the CI/CD job to proceed and is accessed by network.
To make sure this works, the Runner:
- checks which ports are exposed from the container by default
- starts a special container that waits for these ports to be accessible
When the second stage of the check fails, either because there is no opened port in the service, or the service was not started properly before the timeout and the port is not responding, it prints the warning:
*** WARNING: Service XYZ probably didn't start properly.
In most cases it will affect the job, but there may be situations when the job will still succeed even if that warning was printed. For example:
- The service was started a little after the warning was raised, and the job is not using the linked service from the very beginning. In that case, when the job needed to access the service, it may have been already there waiting for connections.
- The service container is not providing any networking service, but it's doing something with the job's directory (all services have the job directory mounted as a volume under
/builds). In that case, the service will do its job, and since the job is not trying to connect to it, it won't fail.
What services are not for
As it was mentioned before, this feature is designed to provide network accessible services. A database is the simplest example of such a service.
NOTE: Note: The services feature is not designed to, and will not add any software from the defined
services image(s) to the job's container.
For example, if you have the following
services defined in your job, the
go commands will not</strong> be available for your script, and thus the job will fail:
job: services: - php:7 - node:latest - golang:1.10 image: alpine:3.7 script: - php -v - node -v - go version
If you need to have
go available for your script, you should either:
- choose an existing Docker image that contains all required tools, or
- create your own Docker image, which will have all the required tools included and use that in your job
Accessing the services
Let's say that you need a Wordpress instance to test some API integration with your application.
You can then use for example the tutum/wordpress image in your
services: - tutum/wordpress:latest
If you don't specify a service alias, when the job is run,
tutum/wordpress will be started and you will have access to it from your build container under two hostnames to choose from:
Note: Hostnames with underscores are not RFC valid and may cause problems in 3rd party applications.
The default aliases for the service's hostname are created from its image name following these rules:
- Everything after the colon (
:) is stripped
- Slash (
/) is replaced with double underscores (
__) and the primary alias is created
- Slash (
/) is replaced with a single dash (
-) and the secondary alias is created (requires GitLab Runner v1.1.0 or higher)
To override the default behavior, you can specify a service alias.
You can simply define an image that will be used for all jobs and a list of services that you want to use during build time:
image: ruby:2.2 services: - postgres:9.3 before_script: - bundle install test: script: - bundle exec rake spec
It is also possible to define different images and services per job:
before_script: - bundle install test:2.1: image: ruby:2.1 services: - postgres:9.3 script: - bundle exec rake spec test:2.2: image: ruby:2.2 services: - postgres:9.4 script: - bundle exec rake spec
Or you can pass some extended configuration options for
image: name: ruby:2.2 entrypoint: ["/bin/bash"] services: - name: my-postgres:9.4 alias: db-postgres entrypoint: ["/usr/local/bin/db-postgres"] command: ["start"] before_script: - bundle install test: script: - bundle exec rake spec
Extended Docker configuration options
Introduced in GitLab and GitLab Runner 9.4.
When configuring the
services entries, you can use a string or a map as options:
- when using a string as an option, it must be the full name of the image to use (including the Registry part if you want to download the image from a Registry other than Docker Hub)
- when using a map as an option, then it must contain at least the
nameoption, which is the same name of the image as used for the string setting
For example, the following two definitions are equal:
Using a string as an option to
image: "registry.example.com/my/image:latest" services: