How to build more secure apps using a dev image for the builder stage in a multi-stage build
Minimus images come in pairs — the fully distroless image with the smallest attack surface and a dev variant that includes more developer-relevant tools such as package managers, a shell, and more.When you build an app using a multi-stage build, you can take advantage of an image pair to create a more secure final result. In the Dockerfile, you can use the dev image in the builder stage and switch to the production image for the final stage. This way, the build process can discard all the unnecessary tools and produce a cleaner, more minimal and secure artifact.
If your app can run on a runtime base after it’s compiled, you can make the app even smaller and more secure. Go (golang) is a great example. See our article on how to use a runtime base.
Using a different base image for the builder stage and the runtime stage has several advantages:
The dev image contains more packages and is therefore more likely to contain more vulnerabilities. For example, we can see that the Python production image has fewer vulnerabilities than the Python dev image.This behavior is consistent across all images, where many times the production image is completely free of vulnerabilities but the dev image might have a few.
When building an app, there is no advantage to including dev packages that increase the attack surface and are not required to run the app. It is preferable to build the application in stages and reduce the final image in size and attack surface. As a result, the final image will have a small attack surface and fewer vulnerabilities, if any.
Just to get a sense of the size differences, the compressed size of the python image is just over 22 MB - compared to 218 MB for the python dev image. Similarly, the SBOM for the Python production image lists 23 packages while the Python dev image lists 73 packages.
Basically, you should use a multi-stage build whenever the opportunity presents itself.
Any compiled or interpreted language is a good candidate for a multi-stage build. This includes (but is not limited to): Python, NodeJS, Rust, PHP, Ruby, etc.
OpenJDK is often used for building Java applications while OpenJRE (open-source Java Runtime Environment) is used to run them. See our Java quick start tutorial