Macadamian Blog

Docker Build Squash vs ONBUILD

Christian Nadeau

While trying to optimize our build system, here is why we chose to use --squash rather than ONBUILD to clean up our Dockerfile.

Docker build Squash vs ONBUILD

We wanted to optimize our build system and reduce image size. We were already basing our images on Alpine version of any tool we need.

NOTE: reference material is accessible here

Why to use the --squash option

We often see Dockerfile which contains statements such as

RUN <install build tools> && \

<build the app using installed tools> && \

<uninstall build tools>

This is logical since docker creates a layer for each command defined in the Dockerfile. But it gets ugly very quickly.
--squash allows us to rewrite the previous example as follow:

RUN <install build tools>

RUN <build the app using installed tools>

RUN <uninstall build tools>

This is A LOT easier to read and there won’t be any impact on the size since all the layers will be squashed. Meaning since the build tools are installed then removed, it will be as if they never existed once squashed.

Before modifications

One thing we are also using is the ONBUILD feature, meaning a base image will only trigger some actions once the image based on it is built.

FROM mhart/alpine-node:7.4

RUN mkdir -p /app

WORKDIR /app

ONBUILD COPY package.json package.json

ONBUILD RUN npm install

Build it:

docker build -t my-onbuild

The image using it is really simple and only needs:

FROM my-onbuild

in order to copy the package.json and perform the npm install.

Build it:

docker build -t mydemo

Squash option

The docker build --squash says:

Squash newly built layers into a single new layer

What is cool about this is that any ONBUILD actions will be part of the derived image, therefore squashed into a single layer.

Simply use:

docker build --squash -t my-onbuild

and

docker build --squash -t mydemo

Why is the derived image not using cache?

That’s the question I had until I realized that squash does NOT check if the built layers were using cache or not, forcing any derived image to rebuild itself.

First build the my-onbuild image using --squash

Sending build context to Docker daemon 2.048 kB

Step 1/5 : FROM mhart/alpine-node:7.4

---> ff43e0491b1e

Step 2/5 : RUN mkdir -p /app

---> Using cache

---> 8f1b0f0b3064

Step 3/5 : WORKDIR /app

---> Using cache

---> 3be72f9e130a

Step 4/5 : ONBUILD copy package.json package.json

---> Using cache

---> 6e821f65bbf2

Step 5/5 : ONBUILD run npm install

---> Using cache

---> 27a4892f2567

Successfully built 27a4892f2567

Second build:

Sending build context to Docker daemon 2.048 kB

Step 1/5 : FROM mhart/alpine-node:7.4

---> ff43e0491b1e

Step 2/5 : RUN mkdir -p /app

---> Using cache

---> 8f1b0f0b3064

Step 3/5 : WORKDIR /app

---> Using cache

---> 3be72f9e130a

Step 4/5 : ONBUILD copy package.json package.json

---> Using cache

---> 6e821f65bbf2

Step 5/5 : ONBUILD run npm install

---> Using cache

---> 27a4892f2567

Successfully built 27a4892f2567

The image ID is the same, why is this happening?

A simple docker history my-onbuild gives us the answer.

First build:

IMAGE               CREATED             CREATED BY                  SIZE               COMMENT

8b19932afd3d       21 seconds ago     0 B     merge sha256:<...>

<missing>           10 minutes ago    /bin/sh -c #(nop) ONBUILD RUN npm install     0 B

<missing>           10 minutes ago     /bin/sh -c #(nop) ONBUILD COPY package.js...   0 B

<missing>           10 minutes ago     /bin/sh -c #(nop) WORKDIR /app                 0 B

<missing>           10 minutes ago     /bin/sh -c mkdir -p /app                       0 B

<missing>           4 weeks ago         /bin/sh -c apk add --no-cache curl make gc...   49.4 MB

<missing>           4 weeks ago         /bin/sh -c #(nop) ENV VERSION=v7.4.0 NPM_...   0 B

<missing>           6 weeks ago         /bin/sh -c #(nop) ADD file:eeed5f514a35d18...   4.8 MB

Second build:

IMAGE               CREATED             CREATED BY       SIZE         COMMENT

f5d8460599b8       21 seconds ago     0 B       merge sha256:<...>

<missing>           10 minutes ago     /bin/sh -c #(nop) ONBUILD

<missing>           10 minutes ago     /bin/sh -c #(nop) ONBUILD <missing>           10 minutes ago     /bin/sh -c #(nop) WORKDIR /app                 0 B

<missing>           10 minutes ago     /bin/sh -c mkdir -p /app                       0 B

<missing>           4 weeks ago         /bin/sh -c apk add --no-cache curl make gc...   49.4 MB

<missing>           4 weeks ago         /bin/sh -c #(nop) ENV VERSION=v7.4.0 NPM_...   0 B

<missing>           6 weeks ago         /bin/sh -c #(nop) ADD file:eeed5f514a35d18...   4.8 MB

The built image content is the same, but the resulting IMAGE ID is DIFFERENT!

So, you should NEVER use --squash in an image using ONBUILD docker command.

Insights delivered to your inbox

Subscribe to the dev blog to get the latest insights in IoT, Alexa Skills development, and software development.

Author Overview

Christian Nadeau

Christian is a veteran software developer at Macadamian with specialties in .NET ( WPF, Silverlight, Window Phone 8 ), java (J2EE, JBoss), C++ (Qt, BB10). He holds a bachelor's degree in computer engineering from the University of Sherbrooke.