GitLab CI Pipeline. Build docker image in pipeline job.

- 9 mins

Photo by Jantine Doornbos on Unsplash

Photo by Rinson Chory on Unsplash

Gitlab allows seamlessly using docker image from public and private hubs. I bet that most of you uses docker executors. All works great and without a hassle until you need to build your own docker image. Fortunately, you can build your docker image automatically in pipeline by leveraging docker-in-docker image build.
I’ll show you how to include docker image build in Gitlab CI Pipeline, push it to Gitlab Repo and use it in another job.



  1. Create Gitlab blank project
  2. Create Dockerfile
  3. Create Gitlab CI pipeline (.gitlab-ci.yml)
    1. Build and push docker image
    2. Use custom docker image
    3. Be efficient!
  4. Summary

Create new GitLab project

I’ll start from scratch. Create new blank project which we will use throughout this blog post. Login into GitLab and navigate to New project -> Create blank project/repository. Give it a project name and hit Create project.

Pages/Plain HTML project

Blank project

Clone the project and we are ready to go.

Create Dockerfile

We need to create a Dockerfile, which will be used to build an docker image. Let’s use Alpine Linux as base image. It is a minimal linux distribution ~5MB. Alpine Linux is great choice when you have specific task to accomplish and you want to use less storage, have fast build times.

Alpine doesn’t have java installed by default - command java -version would fail. We will create Dockerfile to create new docker image based on Alpine with openjdk11 installation. After building and pushing image to repo, we will use it in another job and run command java -version which should run successfully.

Creating new image is not the only option. You can use base Alpine image in your pipeline job, then in script section of the job install java with regular linux command. It will also work. Just keep in mind that that our example is simple. Real world scenario could include multiple software installation and configuration. You wouldn’t want to run it every time pipeline is trigged. Time is precious in CI/CD pipeline. It’s more efficient to build image at first and rebuild it only if Docker file changes.

Dockerfile for alpine with openjdk installation

Our Dockerfile couldn’t be more simple:

You can try build the image locally and test if java is available:

Local Dockerfile test

Local Dockerfile test

Dockerfile is ready. Push it to Gitlab repo or create Dockerfile directly in Gitlab.

Create Gitlab CI pipeline (.gitlab-ci.yml)

We will now create Gitlab CI pipeline and there are two options we could use:

  1. Create a .gitlab-ci.yml file in the root of the repository
  2. Use Gitlab CI/CD editor (in Gitlab, CI/CD -> Editor)

Option 1 is probably used more often, especially in project using a git branch strategy.
Option 2 is more than enough for our scenario and I’ll go with it.

Create New CI/CD Pipeline

Create New CI/CD Pipeline

Click on Create new CI/CD pipeline.
You will directed to editor with an example pipeline. We can use it, but we only need stage section including build and test stages. Remove the rest of the pipline and we can start creating jobs.

Build and push docker image

Pipeline job for building docker image must have 3 main actions:

Pipeline job - docker image build

Currently our pipeline has job for building docker image. Let’s explain it:

First pipeline run for docker image build

First pipeline run for docker image build

You can click on Status to check the logs and more detailed information. Pipeline run succesfully. Let’s check the container registry. Go to Packages & Registry -> Container Registry.

Images in container registry

Images in container registry

You should see an image with a name the same as defiend in $TAG variable inside pipeline job. Under the name there is information that two tagged image exisit. Click on image name.

Tagged images

Tagged images

Two images exisit as expected: one tagged latest and second tagged with branch name (master in mine case).

Use custom docker image

Docker image build is working now as expected and is part of the pipeline. Now we will use newly created image in another pipeline job. Second job will use docker-alpine-java image and run script java -version to see that in fact we are using our custom alpine image with java installed.

Pipeline job - use custom image

Testing custom image job is quite simpler.

Go to the CI/CD -> Pipelines, then click on last one to see that two jobs were done succesfully (build and test). Click on test job to check the logs. / We are looking for two interesting parts:

Be efficient!

You might have noticed a flaw in pipeline. When pipeline run at first it had built alpine-java image and pushed it to repository. When pipeline has run the second time it built alpine-image again, even though it was not necessary. It’s casued by not having any rules defining when to run a job in piepline.
Fortunately it is a quick fix. Add a rule section in build:docker-alpine-java job. The rule will allow job to run only if there is any change in Dockerfile.

Pipeline job - rule

If you now check latest pipeline run you will notice that only test:alpine-java was run in pipeline. Pipeline with rule

Pipeline with rule

Moreover, thanks to the flaw we have just fixed, you can compare diffrence in duration of two docker build jobs.

Build jobs duration

Build jobs duration

Maybe you already figured why the build job took 1 min 30 sec when was run first time, but only 1 min when run second time? The answer is cache. Second run built exactly the same image as the first run, beacause there was no diffrence in Dockerfile. Since we implemented cache usage in docker image build job, the second run was much quicker.


Throughtout the blog post we have succesfully:

Thanks for reading!

Karol Filipczuk

Karol Filipczuk

DevOps and Cloud

comments powered by Disqus
rss facebook twitter github gitlab youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora