Choosing a Docker Base Image

When you’re developing a new Docker project, you might default to your favorite base image. There are a few reasons why you might want to consider a few others, especially when it comes to security, maintainability, and performance.

/images/blog/cover-images/choosing-img.png
What's the best docker base image for me?

by on

The first line you’ll likely write in your Dockerfile is the one where you define its base image. This is the FROM <image> instruction. In fact, you can’t write a Dockerfile without the FROM instruction. Naturally, we usually don’t overthink this or let it become a blocker; we define it then and there.

Out of habit, many of us pick a favorite base image and never look back. It makes sense: when you’re trying to get an app/service containerized, is it worth it to spend time stressing over choosing the perfect base image? Any performance differences between images might seem trivial anyways.

Well, yes and no. There are a myriad of reasons why it’s well worth your time to choose a base image wisely. We’ll get into that shortly.

What is a Docker base image?

Quick recap: a Docker base image is the “foundation” of your Dockerfile. It’s a copy of an existing image that you’ll be building on top of. You’ll have access to its filesystem, config, and dependencies, which is useful for reducing the amount of files you import.

For example, if you want to run a Python app without importing common Python tools and dependencies, the python base image makes this easier.

When you define your base image in your FROM instruction, Docker will pull that image from Docker Hub, unless otherwise specified.

Do I really need a base image?

If you don’t want to use a base image, you can start your Dockerfile with a FROM scratch instruction. Be warned: Docker’s scratch is entirely empty. You’ll have to do everything yourself: configuring an OS, building out your own filesystem, etc.

Starting “from scratch” with Docker is helpful if you’re ready to develop your own secure, lightweight Docker image. However, most of the time this is total overkill. There are better options for lighter images, especially minimal and distroless base images.

What goes into choosing a base image?

Users often have three primary criteria when selecting a Docker base image for production:

  • Type of application
  • Image security
  • Image performance

This means there isn’t exactly a one-size-fits-all Docker base image, but some might be better for your use case than others. And if you’re using it in production, you’ll have fewer scenarios in which you’re debugging/iterating, so you may want to optimize for security and performance above all else. For this reason, many production images are distroless or at least minimal.

However, when choosing an image for development, you’ll have different priorities. Ease of setup suddenly becomes a big factor, followed closely by local performance.

If you’re heavily developing within your Docker container, you might want some preexisting developer tools, package managers, and a shell. This makes something like ubuntu appealing: it ships with a few hundred tools and libraries, many of which you’d import anyway.

Minimal Docker images

Minimal images have a smaller footprint than standard images. They contain fewer dependencies and tools, which naturally reduces the number of vulnerabilities they have and makes them build/deploy faster.

The most popular minimal image is alpine, which is the Dockerized version of Alpine Linux. Many common base images also have an alpine variant, e.g. node:<version>-alpine, postgres:<version>-alpine, etc. Alpine is only 5 MB and has zero CVEs (common vulnerabilities and exploits).

A couple other minimal images worth checking out are busybox and Red Hat’s ubi9.

Distroless Docker images

Distroless images are especially favored for secure software. They contain the absolute minimum required to run your program. They also don’t have a shell or package manager, which distinguishes them from other minimal images.

This makes them tricky to develop with, which is why they’re primarily used in production. Conveniently, many distroless images offer a dev counterpart: the same image, but with a shell and some common devtools. That way, when you make your Docker image production-ready, you simply change FROM my-distroless-dev to FROM my-distroless.

Google and Chainguard both maintain open source distroless images. You can browse Google’s distroless base images in this repo, and view Chainguard’s image directory (not all, but many listed here are distroless).

Image security 101

At the end of the day, the biggest differentiator between different Docker base images is their contents. More contents == easier setup, and fewer contents == better security.

Container security experts suggest choosing an image with as few contents as possible, and building from there. This isn’t as beginner-friendly and takes some trial and error. Why is it so important?

Every day, new software vulnerabilities are exposed in common packages. It can be hard to keep up-to-date with them, especially if you have a bloated base image with hundreds or thousands of libraries. Having more libraries increases your attack surface, and therefore your odds of having a vulnerability in your image.

You can stay ahead of vulnerabilities by running an image scanner in your CI/CD pipeline, like Aqua Trivy, Docker Scout, or Snyk. These will let you know which CVEs your image contains, and how severe they are. You can then resolve as needed. And if you’re using a minimal or distroless image, vulnerabilities will be few and far between.

TLDR: Making the base image decision

To quickly recap: you’ve got a lot of choices for a good Docker base image. For development, prioritize an image that has the packages you need (e.g. based on the language you’re using, or one that contains some standard devtools). If you’re looking for a quick setup so you can start developing right out of the box, you might like:

And if you’re looking for something secure and efficient for production use cases, you’ll want to look for minimal base images and BYOD (bring your own dependencies):

Better yet, many images offer multiple variants, so you can use the same base for dev/prod and easily toggle between both the lightweight and full-featured flavors.

And when in doubt, use Alpine! It’s one of the top base images for a reason: it’s lightweight, easy to use, frequently updated, and has very few vulnerabilities.

Try Shipyard today

Get isolated, full-stack ephemeral environments on every PR.

What is Shipyard?

Shipyard is the Ephemeral Environment Self-Service Platform.

Automated review environments on every pull request for Developers, Product, and QA teams.

Stay connected

Latest Articles

Shipyard Newsletter
Stay in the (inner) loop

Hear about the latest and greatest in cloud native, container orchestration, DevOps, and more when you sign up for our monthly newsletter.