In Kubernetes, our applications are built and run as containers. Each container comes from an image, and each image is built from layers of other images. This layered process is called the software supply chain. Just like a factory assembling a product from parts, container images are assembled from smaller components. If even one part is flawed or tampered with, the final product can be unsafe. That’s why securing the supply chain is critical—it protects our applications from hidden vulnerabilities.
In this blog post, we’ll explore what base and parent images are, and how they shape the containers we use. We’ll look at best practices for building secure images, and how to choose the right parent image for our apps. We’ll also learn how to reduce risk by keeping our images small, modular, and stateless.
Base vs Parent Images
When we create a container image, we don’t start from zero. We use something called a parent image. This is a starting point that already has some software or libraries inside.But here’s the key: parent images also have their own parents. This chain continues until we reach the very first image—called the base image. It’s usually built from scratch, meaning it starts empty and adds only what’s needed.
Let’s look at an example:
httpd → debian:buster-slim → scratch
httpd
is the web server image we want to use.- It’s built on top of
debian:buster-slim
, a lightweight Linux system. - That Debian image was built from
scratch
, the base image.
Best Practices When Building Container Images
Let’s have a look at some general recommendations to build safe images1. Modularity
Avoid building images that include multiple applications, such as combining a web server with other background services in one image. Instead, focus on building modular images that solve a single problem at a time. For example, use one image for the web server and a separate one for the database. This way, each image contains only the libraries and dependencies it needs, without being tied to other components.When deployed as containers, these images come together to form a complete application made of distinct services. With this modular design, we can scale or update each service independently, without disrupting the rest of the system.
2. Stateless Containers
Containers are ephemeral in nature, we should be able to destroy them and bring them back online without losing any data. This does not mean that our applications should not deal with data or states, it wouldn’t make sense. It means that, for those applications, we should store data and states in an external volume or some kind of caching service3. Choose the right Parent Image
Choosing the right parent image is one of the most important decisions. Let’s break it down:- Match Technical Needs - Start by checking your app’s requirements. For example, if your app runs on Java, find an image that includes the Java JDK.
- Authenticity - Always choose official images or images from a verified publisher. These are tested, trusted, and built from known sources.
- Up-to-Date - Security threats are always changing. Use images that are updated often. These images fix bugs and patch known security holes.
4. Keep It Small
Keep image size as small as possible. Smaller images are pulled faster and deployed more efficiently. Use minimal versions of official operating systems whenever available. They reduce complexity and limit potential vulnerabilities.Only include the libraries and dependencies that the application strictly needs. After installing them, clean up any leftover files. Remove tools like
curl
or wget
that could let an attacker download files. Also remove shells, network utilities, and text editors that are not needed for the app to run. Package managers like apt
or yum
should also be excluded, as they can be misused to install malicious software inside the container.Avoid adding unnecessary files. If you need debugging tools or extra packages during development, do not include them in your production image. Instead, use separate images for production and non-production environments.
Google’s official Distroless images are a good example of this approach. These images contain only the application and its runtime—nothing else. Fewer packages mean fewer vulnerabilities. We can verify this by scanning both minimal and non-minimal images with a tool like Trivy and comparing the results.