Building Custom Images for Docker Executor
CircleCI 2.0 gives you access to the power and flexibility of Docker. One of the ways you can take advantage of this is to create custom Docker images for your jobs. The benefits of doing this are:
- Faster job execution because you can preinstall all the tools you require, eliminating the need to install them on each job run
- A more concise and easier to maintain CircleCI
In this document we will give a walkthrough how to create a custom image. In most cases you’ll want to have a custom image for your primary container so we’ll mostly describe this case. But you can easily apply this knowledge to create images for supporting containers as well.
As a prerequisite you’ll need to have Docker installed. Please follow the official Docker guide.
If you are unfamiliar with Docker, we recommend reading Docker’s getting started guide.
Docker has a special format for describing images and conventionally this file is named
Dockerfile. We recommend keeping this file together with your project source code in
.circleci/images folder. For instance, in our Docker demo project we put the
Dockerfile for the primary container into
First of all you need to choose a base image. In general it makes sense to use a image with your main language/framework as a base images. Docker Hub has pre-built images for most popular languages and frameworks. We recommend starting with an officially supported image.
Once you’ve chosen a base image you can start writing a
Dockerfile to extend it:
For example, in our Docker demo project we use
golang:1.8.0 because the project is using Go.
Read more about
Now you can add the tools required for your job. You can do this using the
RUN apt-get update && apt-get install -y netcat RUN go get github.com/jstemmer/go-junit-report
In our example project we use
netcat to validate that our database is up and running. The
golang:1.8.0 base image doesn’t have it preinstalled, so we specify it in our
Dockerfile. Next we install a special Go library for generating test reports
Note: For images not based on
Debian-like distributions, the command for installing additional applications might be different. For instance, for
Alpine based images the same tools might be installed using:
RUN apk update && apk add netcat-openbsd git RUN go get github.com/jstemmer/go-junit-report
Read more about
There are a few tools a custom image needs to have in order to be used as a primary image in CircleCI:
In the future we will simplify this, but right now you need to make sure that these tools are present in a custom image. You might not need to install all of them, the base image you choose might have some of them pre-installed.
Sometimes you might want to add custom files/tools not present in package managers. You can do that using
ADD custom_tool /usr/bin/
In this case we copy
custom_tool into the
/usr/bin/ directory of an image. Please note that
custom_tool needs to be in the same directory as
Read more about
Building the image
Once all required tools are specified in
Dockerfile we can build the image.
$ docker build <path-to-dockerfile>
You’ll see how all commands specified in
Dockerfile are executed. If there are any errors they’ll be displayed and you’ll need to fix them before continuing. If the build is successful you’ll have something like this at the very end:
... Successfully built e32703162dd4
Read more about
docker build command.
Congratulations, you’ve just built your first image! Now we need to store it somewhere to make it available for CircleCI.
Storing images in a Docker Registry
In order to let CircleCI use your custom image you need to place it in a public Docker Registry. The easiest way to do that is to create an account on Docker Hub. Docker Hub allows you to store unlimited public images for free. If your organization is already using Docker Hub you can use your existing account.
Public or private images?
Please note that in order to use an image with our Docker Executor you’ll have to have a public repository. If you want to keep your image private please read about using private images and repositories.
Using different Docker registries
Our example uses Docker Hub, but you can use different registries if you prefer. Please adapt the example based on the registry you’re using.
Preparing the image for the registry
Once you have your Docker Hub account you can create a new repository. We recommend to use a pattern like
<project-name>-<container-name> for a repository name (for example,
Now you need to rebuild your image using your account and repository name:
$ docker build -t circleci/cci-demo-docker-primary:0.0.1 <path-to-dockerfile>
In this case we are using
-t key to specify the name and tag of our new image:
circleci- our account in Docker Hub
cci-demo-docker-primary- repository name
0.0.1- tag (version) of the image. Always update the tag if you change something in a
Dockerfileotherwise you might have unpredictable results (read more)
Pushing the image to the registry
Now we can push the image to Docker Hub:
$ docker login $ docker push circleci/cci-demo-docker-primary:0.0.1
Note that first we use
docker login to authenticate in Docker Hub. If you use a registry other than Docker Hub, please refer to its documentation about how to push images there.
Using your image on CircleCI
Once the image is successfully pushed you can start using it in your
version: 2.0 jobs: build: docker: - image: circleci/cci-demo-docker-primary:0.0.1
If you have any questions, head over to our community forum for support from us and other users.