5x Smaller Java Docker Images — 2025 Optimization Guide
Transcript:
If your Docker images are bigger than your Netflix watch list, we need to talk. Big Docker images slow deployments, pose security risks, and waste resources. So, let's make them lean, clean, and lightning fast. Stay with me and I will make a Java Docker container image five times smaller right in front of you.
So, why do Docker images get so huge? Well, think of the reasons for bloated images as criminals. Two and more blossers form a gang that is hard to take under control. And here's the hateful eight of Java Docker images. Huge base image, unnecessary OS packages, unnecessary image layers, single base image for building and running the application, leftover OS cache, stray files, unnecessary dependencies, unnecessary JDK modules. Now that we know the gang members by site, let's get rid of them.
Okay, so this Docker file is going to be our starting point. It is wrong on so many levels that I don't even where to start. I know wait, I know. First thing first, don't do that. Don't use the official Docker image of Open JDK. It is marked as deprecated and is no longer supported. It doesn't receive any updates or bug fixes. So step zero if you use this image migrate to a supported open JDK distribution. Luckily several good options are available. Libera JDK, Azul Zulu, Eclipse Tamarind, IBM samur runtimes, Amazon caret to name a few. In this video I will use Liberica JDK.
So let's change the from instruction. Let's build the image to see where we are starting at. Wow, that's one big image. All right, we are ready. The base image with Debian is too big. Which takes us to the first step. Choose Linux base. There are two ways to reduce the size of the base image. One of them is to use DRS images. Another is to use a lightweight Linux distra for the cloud. DRS images deserve a separate video actually. So write in the comments if you want me to make one. For now, let's take the beaten track and use a lightweight Linux DRO. Just like with JDKs, there are multiple Linux distras for the cloud. Debian Slim, Alpine, Alpakita, Ubuntu, Jami. They all are different and they all have different size. So I recommend you try all of them and uh see which one is best for your purposes. Your base image choice should be as thoughtful as picking up a Netflix series. Okay, so the smallest of those distros I mentioned are Alpine and Alpakita. They are so small that your Java docker image will shed several kilos, I'm sorry, megabytes immediately without any diets or exercises. I wish I could simply choose a small Linux for my tummy. For my demo, I will take Alpakita because there's a special container image called Libera runtime container which includes Alpakita Linux and Libera JDK light optimized for the cloud. You know what that means? Even better weight shredding. Let's change the front instruction again and build the image. Let's look at the results. Awesome. We saved 200 MGB, but I'm still not quite happy. You know why? We use the same base image for building and running the application. It means that we leave behind a huge pile of unnecessary files. Let's fix that.
Have you ever cooked an exquisite dinner leaving behind a mess of a kitchen? That's what happens to your Docker image if you don't remove unnecessary OS packages, modules, files, and image layers. First, let's talk about image layers. Each layer of a Docker image represents an instruction in a Docker file. Instructions that modify a file system create a new layer. So each run, copy, and add command adds a new layer to your image. And so thus increasing its size. So instead of running several run statements one by one like in this example, we can simply merge these instructions. What about files? When you are building a Docker image, the process copies files from your computer into something called the build context. Now, here's the thing. Your project probably has a lot of large files that your application doesn't need to run. And that's where the Docker ignore file comes in. It is a very simple way to tell Docker which files to skip when copying. You can also use RM to delete a file from the Docker image. There's one more thing to keep in mind. Package managers have a cache where they keep installed packages and files, but you don't need this cache in your final Docker image. So, make sure you clean it out. You can clean the cache with the AP package managers or you can add the no cache flag with the APK package manager. APK is used in Alpine and Alpakita and similar distributions. With a no cache flag, no packages will be saved into the cache in the first place. By the way, the number of packages included into the base image by default depends on a Linux distro. Ubunt for instance is bigger because it contains more packages. So you may want to remove some of them. Alpine and Alpakita and similar distros however are minimalistic which means that you may have to add necessary packages. Okay, this is all very well, but are you ready for an even sleeker approach to removing unnecessary stuff from your Docker image? Let's multi-stage.
Well, the first question is, what are multi-stage builds? The concept behind multi-stage builds is simple. You write a single Docker file, but use several from statements instead of one. Each one uses its own base image and kicks off a new build phase. Now, here's the key part. You can choose which files to copy from one stage into another. What this means is that your final image stays clean. It won't have all those extra layers, packages, and files that were cluttering things up in the previous stages. So, you will have only what you really need. Think of multi-stage builds as packing for a trip. Take only what you will actually use or else you will have to pay for the overweight package or additional cloud space for that matter. Let's implement multi-stage builds in our docker file. The first image uses library runtime container with the JDK to build the application. At the second stage, we take librar which is required for running the application and we copy only the jar file into the final image. Let's check the results. Brilliant. Love it. But we are not done yet. There are more things to shred. Let me introduce to you the slim toolkit, the Marie Cond of Docker. This tool automatically optimizes the size of the Docker image. It creates a temporary container and decides which files the application requires using various analysis techniques. Let's use Docker Slim with our container based on Libera JRE. Nice. The promise is to reduce the image size by up to 90%. Well, we actually cleared out a lot of stuff, but still a nice touch, but not a final touch. Of course, you should use this tool with caution. Slim can accidentally throw away files that the application actually needs due to lazy loading. But don't worry, the documentation describes how to avoid such situations. Still not enough, I know. Let's cut out a custom Java runtime environment.
Okay, remember that at the final stage we used the base image with JRE that contains JVM and packages necessary to run a Java application. But we can actually go even further and remove all the packages that our application doesn't use. How? With the JLink tool. It analyzes the dependencies your application uses and cuts out a custom JRE. I love tailor made things. First, let's create a jar file and see which modules our application uses with the help of JDAPS. Now, let's specify these modules in our Docker file. Okay, let's build the image. I can't wait to see the results. Voila. Our Docker image now takes only 143 MGB. And that's the power of JLink. What if we combined JLink and Docker Slim? For the first time ever, friends, we will go the whole way. Now that's what I call the ultimate wet shredding workout. Okay. So let's take the Docker image we have built with the help of JLink and use Docker Slim with that. Ready to see the results? I know I am. Wow. 116 MGB. I promised. I delivered. Five times smaller Docker image. But wait, wait, wait. Don't just go yet. Give me a couple more minutes and I will tell you about the common mistakes when reducing the Docker image size. Without these tips, you risk turning wet shredding into butchering. Small Docker container images are great only if they work properly. Remember the golden rule of software development. Don't overoptimize.
So here are some things to consider. Use Docker Dive or similar tools to inspect the contents of your image. This way you will understand which layers require optimization. Keep in mind that Alpine is based on muscle library. It may not be compatible with some workloads. Alpakita also uses muscle, but it also offers the glyc based flavor. So test out different images and see which works best with your app. Don't delete files and packages aggressively. Delete only those that you or your application won't actually need. Use docker slim wisely or else you may end up with a dummy container image. albeit a very small one. All right, friends, time to summarize. You have learned how to build small, secure, and costefficient Docker container images. Your containers are now in better shape than some fitness bloggers out there. Before you go, don't forget to like, comment, and hit the subscribe button. See you next time.