Tips to run Docker Build faster

Vicky AV
5 min readDec 18, 2020
Image taken from https://foxutech.com/how-to-build-a-docker-image-using-dockerfile/

Let’s assume we are containerising an Angular application & we created Dockerfile at project’s root directory

FROM nginx:1.17.1-alpineCOPY nginx.conf /etc/nginx/nginx.confCOPY /dist/my-app /usr/share/nginx/html

Lets build our Angular project first

> ng build --prod

After build, project directory looks like below

Project Root Directory Snap

Lets build the Docker image now

> docker build -t my-img .Sending build context to Docker daemon  378.9MBStep 1/3 : FROM nginx:1.17.1-alpine---> ea1193fd3ddeStep 2/3 : COPY nginx.conf /etc/nginx/nginx.conf---> 7c915599a748Step 3/3 : COPY /dist/my-app /usr/share/nginx/html---> 1db9733be99bSuccessfully built 1db9733be99bSuccessfully tagged my-img:latest

The docker build command approximately took 53 seconds to run 😱

On side note, you can calculate time taken to run any command in Linux/Mac by appending the word time before your command

> time docker build -t my-img .

The nginx image we used for building our Docker image is just 20.6MB in size . Why is it taking 53 seconds to build such a small Docker image ?

The culprit here is the build context

Understanding Build Context

In docker build -t my-img . command, you could have noticed .(dot) at the end which implies

  • Dockerfile resides in the current directory
  • Build context is set in the current directory

Docker build command will first compress and create a tar file of everything inside Build Context’s directory. Then that tar file will be sent to the Docker Daemon for building Docker image

Since we kept Dockerfile in the project root directory, build command is creating tar file of entire root directory of the project (including node_modules folder 😱)

Thats why we are seeing Sending build context to Docker daemon 378.9MB in output, meaning 378.9 MB transferred to Docker daemon for building the docker image by the build command

Note, Docker Daemon could be running either in our local system or in remote server. If its in remote, we will be sending 378 MB data to Docker Daemon in remote server for every single build command

How to avoid this ?

Don’t create Dockerfile in project root directory ! Always create Dockerfile or docker-compose file inside Docker folder

Whats the catch with this solution ?

Dockerfile can only refer to files/folders available in its current or its subdirectories

E.g. — Angular/NodeJS Application

We need contents of dist folder to build our Docker image. The dist folder usually gets generated in root directory by ng build command

If we moved Dockerfile to Docker folder, we can’t refer dist folder from Dockerfile

How to fix this ?

Go to angular.json file & change the “outputPath”: “dist/my-app” to “outputPath”: “Docker/dist/my-app”

This will ensure the dist folder getting generated inside Docker folder all the time

Dockerfile & dist folder inside the Docker folder

After doing above step, Lets run docker build from inside Docker folder

> ng build --prod> cd Docker > docker build -t my-img .Sending build context to Docker daemon  273.4kBStep 1/3 : FROM nginx:1.17.1-alpine---> ea1193fd3ddeStep 2/3 : COPY nginx.conf /etc/nginx/nginx.conf---> Using cache---> 7c915599a748Step 3/3 : COPY /dist/my-app /usr/share/nginx/html---> Using cache---> 1db9733be99bSuccessfully built 1db9733be99bSuccessfully tagged my-img:latest

Note :

  • Only 273.4kB sent to Docker daemon from Build Context
  • Build completed in just 1.079 seconds 🔥🔥🔥

E.g — Spring Boot Application

Consider a Spring Boot app with Dockerfile created in project root directory

FROM openjdk:8-jdk-alpine
COPY target/EmpApp.jar EmpApp.jar
EXPOSE 8080
CMD ["java", "-jar", "EmpApp.jar"]

Project structure looks like below

Spring Boot App with Dockerfile in project root directory

Let’s build the Docker image

> mvn clean package> docker build -t emp-img .Sending build context to Docker daemon  81.33MBStep 1/4 : FROM openjdk:8-jdk-alpine
---> a3562aa0b991
Step 2/4 : COPY target/EmpApp.jar EmpApp.jar
---> 13f2e0c2b88f
Step 3/4 : EXPOSE 8080
---> Running in 3062c4ae9244
Removing intermediate container 3062c4ae9244
---> 30c5ad3077e2
Step 4/4 : CMD ["java", "-jar", "EmpApp.jar"]
---> Running in 6fb88cbdcf04
Removing intermediate container 6fb88cbdcf04
---> 4b1e9218d37c
Successfully built 4b1e9218d37c
Successfully tagged emp-img:latest

Build command is sending nearly 81 MB tar file (including src, target & even .idea folder) to Docker daemon and entire build took approximately 12 seconds to run

Now let’s move the Dockerfile to Docker folder. Remember, we still need the JAR file from target folder to build Docker image

So lets configure the spring-boot-maven-plugin to create JAR file inside Docker folder instead of target folder

In pom.xml

<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<outputDirectory>${project.basedir}/Docker
</outputDirectory>

<finalName>EmpApp</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>

Now mvn clean package will create EmpApp.jar file inside Docker folder

Dockerfile & EmpApp.jar file inside Docker folder

Lets run the docker build command again & see the results

> mvn clean package> cd Docker> docker build -t emp-img .Sending build context to Docker daemon   40.5MBStep 1/4 : FROM openjdk:8-jdk-alpine
---> a3562aa0b991
Step 2/4 : COPY EmpApp.jar EmpApp.jar
---> ea534aea1444
Step 3/4 : EXPOSE 8080
---> Running in 29eb70ec1691
Removing intermediate container 29eb70ec1691
---> 7abfcf1d18ff
Step 4/4 : CMD ["java", "-jar", "EmpApp.jar"]
---> Running in fdf9316244dd
Removing intermediate container fdf9316244dd
---> 83a17ced023a
Successfully built 83a17ced023a
Successfully tagged emp-img:latest

Now Docker build only sends 40 MB (size of EmpApp.jar) to Docker Daemon & the entire build took only 4.49 seconds

Thats all folks !!!

--

--