The apt sandwich

Brett Weir, March 2, 2023

Sandwich synopsis

When crafting Dockerfiles for Debian-based containers, you'll frequently run into snippets that look like this:

# hadolint ignore=DL3008
RUN apt-get update -y \
    && apt-get install -y --no-install-recommends \
        inkscape \
    && rm -rf /var/lib/apt/lists/*

This is what I like to call the apt sandwich. I call it that because it consists of whatever you're actually trying to do (e.g. install some packages), with apt-related boilerplate on either end.

Sandwich specifications

The prototypical apt sandwich looks like:

# hadolint ignore=DL3008
RUN apt-get update -y \
    && apt-get install -y --no-install-recommends \
        # ...
        # the sandwich filling - the packages you want to install
        # ...
    && rm -rf /var/lib/apt/lists/*

Here what is accomplished with this snippet:

All of this is done in a single command to keep intermediate files from being snapshotted by Docker and becoming a permanent part of the image.

Sandwich savings

Using the apt sandwich doesn't just look niceā€”it also makes your containers a lot smaller and build faster as well!

Don't take my word for it though. In this section, we have a list of sample Dockerfiles you can build yourself to see the impact it can have, and we'll list the image size and build time for each Dockerfile. To follow along:

Okay, now you're ready to build some images!

To put it all together:

Command Size Size Reduction Time Time Reduction
Install inkscape 594MB 106s
Clean package index after install 552MB 42MB (7%) 103s 3s (3%)
Don't install recommended packages 358MB 236MB (40%) 70s 36s (34%)
Base image 78MB

Cleaning the package index and avoiding installing recommended packages has a pretty dramatic impact:

Sandwich summary

apt commands should pretty much always be written this way in a Dockerfile. It will result in a lot of savings in size, bandwidth, and time, which translates to savings in cost as well.

Containers will build faster, take up less space, and deploy into a target environment faster. And, since you know what this idiom is supposed to look like, it'll be easy to spot when a Dockerfile doesn't get it quite right, and you'll be able to quickly improve the container performance.