DEV Community

Marcos Brizeno
Marcos Brizeno

Posted on

5 1

Optimizing docker to build and run your application

Docker is a great tool to for running applications without worrying about dependencies, but it can also be used to build your application. In this post I'm gonna share a nice technique I've used to build and run a web app with Docker and stopped worrying about dependencies.

Building vs Running

Building an application usually requires a lot more dependencies than just running it and most languages and frameworks offer a way to build your application bundling everything you need to run it.

As an example a Java application can be compiled to a single .jar file, Go apps can be compiled to a binary and even applications in Ruby can be bundled in a SO package (like rpm). This makes it a lot easier to run the application, and even more so if you use Docker.

Once your application is bundled, you can just copy it to a Docker image and have docker run it for you - and only worry about installing Docker itself. What about actually building the whole app in a Docker image so you don't even need to worry about the dependencies even on your machine (or on a CI server)?

The main problem with building your app on the same image that will be used to run it is that this image will have to have everything required for both building and running.

Every command you add in a Dockerfile will add a layer to your image. Think of a Docker image as a git repository, the very first FROM command is the initial commit for that repo, each command after that is like a new commit that will potentially increase the size of your repository. The more commands you have in a Dockerfile the bigger your image might be.

So, how about using two different images? One for building and another one for running?

Building and Running

I was working on a small web app (using Java and Spring Boot) and while searching for best practices around docker I ran into this very useful video. It talks about different ways to reduce the size of your image (which is specially useful in the context of Kubernetes).

So I decided to apply these techniques to build and deploy this Java/Spring Boot web app, using one image to build the .jar file and another one to run it. We used gradle on this project so my first step was to use the gradle base image to build the app.

FROM gradle:jdk8 AS build-env
# Setup ARGs and ENVs
# Some RUN commands
# Copy the code
COPY --chown=gradle:gradle . .
# Some more RUN commands
# Finally, build the app
RUN ["./gradlew", "build"]

This image is actually pretty big. First of all it uses a base image that comes with a lot more stuff (comparing to the -alpine alternative), because I actually need tools like curl and others that I'd have to install myself if I had used a thiner version.

And that's actually the point of using a build image, you don't need to worry about what's there because it will be discarded once the final image is built.

Now I can define the image that will run the application:

FROM openjdk:8-alpine
COPY entrypoint.sh entrypoint.sh
COPY --from=build-env /home/gradle/build/libs/my-app-0.0.0.jar my-app.jar
ENTRYPOINT [ "sh", "entrypoint.sh" ]

This is a pretty thin image, it uses the 8-alpine version of openjdk, which is much smaller and does the job well, since Java is pretty much the only runtime dependency.

Both of these declaration live on the same Dockerfile, note how the build image has FROM ... AS build-env at the beginning. This will create a temporary image with a build-env alias and since Docker only considers the last FROM statement to be the actual image it will be discard afterwards. Then on the runtime image it copies from the build image with COPY --from=build-env.

And there you go, building and running an app using a single Dockerfile while optimizing for each context. Using this pattern can even help simplify your CI by only having to install Docker on the agents.

Thanks for taking the time to read this and hope it helps you to improve your own images :)

Neon image

Build better on Postgres with AI-Assisted Development Practices

Compare top AI coding tools like Cursor and Windsurf with Neon's database integration. Generate synthetic data and manage databases with natural language.

Read more →

Top comments (0)

Jetbrains image

Build Secure, Ship Fast

Discover best practices to secure CI/CD without slowing down your pipeline.

Read more

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay