Streamline your container workflow with Codefresh!

Webinar: Creating an Efficient Docker Build Pipeline for Java Apps

Docker Tutorial | May 13, 2017

In our last webinar, we showed how to create an efficient Docker build pipeline for Java apps. We found that a lot of “Docker for Java developers” tutorials out there unintentionally encourage some Docker bad practices. So we had our Cheif Researcher, Alexei Ledenev, demonstrate how to craft the perfect Java-Docker build flow to consistently produce small, efficient, and secure Docker images.

View the webinar on-demand to learn:

  • The Docker Builder Pattern
  • How to define all pipeline steps and Dockerize them
  • How to pass build context between steps
  • How to Automate your build pipeline
  • Java tips/ best practices for each step

Link to Alexei’s slides

Highlights of Alexei’s Talk:

Common Pitfalls

Most new Docker users start with a “Naive Approach” when Dockerizing their application and use a familiar VM build and install flow. In fact, there are many blog posts and articles teach you to do this. They usually start with some kind of Linux image, and first install all required packages, Oracle JDK, etc. Then they copy all project files into this box, run maven install, and define the command line or how the app should be executed. This is a straight forward process but what you’re actually left with is a huge image size. In this case, the image is 1.3 GB in size and took me 30 minutes to build on my laptop.

Another approach many people use is the “Standard Approach.” Meaning they take an official Docker image provided (by a company or community), in this example, it’s an OpenJDK image, they install maven, do one little trick -add a pom.xml file, and then install all dependencies. This step is important because each command in a Dockerfile creates an additional layer. Docker will try to re-create this layer with each build. But if you add the pom.xml file it will use the cache from the previous build if nothing has changed.  This allows us to slightly reduce the image size to 1.2 GB. It’s still big but slightly smaller than the last and now our build time is around 15 minutes.
It’s important to understand containers are not VMs. 

“A Linux container is nothing more than a process that runs on Linux. It shares a host kernel with other containerized processes.” –Joe Fernandes, Senior Director, OpenShift Product Management, Red Hat

Once you think of your application as a process, you need to ask -what is the bare minimum that is required to run my application? Using Docker technology you can pack all the files and tools needed to create your application process (the resource files, configuration files, etc). You can isolate the entire process in a separate container so that your app feels like it’s running alone.

What is the bare minimum required to run Java App?

In this example, we need to package the following in order to create the process to run our Java application:
  1. Base Image with C Runtime and Posix shell (Alexei recommends Alpine)
  2. Java Runtime Environment (OpenJDK JRE)
  3. Application byte-code and resources (app.jar)
  4. 3rd Party Libraries (lib/*.jar)
  5. Optionally HTTP server (Alexei recommends Tomcat, Jetty, or Netty, not enterprise servers since their design wasn’t built for containers)

Why is your Docker image size important?

  • Time to build
  • Network latency
  • Storage
  • Service availability and elasticity
  • Security
  • Development agility

 Docker Builder Pattern

Alexei explained how to use the Docker builder pattern to separate our build tools and runtime.

Java Docker Builder

Our Java Docker builder should contain the following:

  1. Base Image with C Runtime and Posix shell (Alpine)
  2. Java Development Kit (OpenJDK)
  3. Javac or other JVM compiler (Scala, Kotlin, …)
  4. Build Management Tool (Maven, SBT, Gradle, …)
  5. Linters, code scanners, test frameworks, test tools,
 Once you Dockerize your build environment you can deploy it and use it and run your app on any machine without the need to install anything manually. This eliminates the “it works on my machine” problem.

Maven Builder Dockerfile

Our builder Dockerfile looks like this:
Once we capture our build process, we can consistently produce the results (it captures the source code at a specific point in time so we won’t be impacted by any changes).

Java App Dockerfile

Our Java application Dockerfile is pretty simple because we are only including the bare minimum we need to run it. In this example, we only need the following 3 lines of code.
So now our builder image is only 263 MB, and our runtime image is 143 MB. This is almost 10x smaller the 1st image.

Build Pipeline Orchestration

Once you have both Dockerfiles ready, you can automate your build pipeline.
Alexei explains how to automate it with a Makefile like this:


In the demo, Alexei shows how to easily automate your pipeline using a Docker-native CI/CD like Codefresh to consistently produce small, efficient, and secure Docker images.

(See demo – 32 minutes 25 seconds of the webinar)

He shows two ways to automate your pipeline in Codefresh.


  1. Using a Codefresh YAML file like this:
  2. Using the Codefresh UI
The Codefresh cache allows us to speed up the build process. After all the tests have passed, Codefresh takes the produced VAR files and packages them into a final Docker image. It then tags and labels the image with the git branch and commit info, and pushes it to Alexei’s private Codefresh registry (or any registry you choose).

Additional Resources

About Dan Garfield

Dan is a full-stack web developer and VP of the Marketing at Codefresh. Dan is a *nix native and all around technology enthusiast.

Reader Interactions

Enjoy this article? Don't forget to share.


Your email address will not be published. Required fields are marked *

Follow me on Twitter