3 simple tricks for smaller Docker images

Daniele Polencic
Oct 8, 2018 · 10 min read
Image for post
Image for post
FROM ubuntu
RUN apt-get update && apt-get install vim
FROM ubuntu
RUN apt-get update
RUN apt-get install vim
Image for post
Image for post

1. Squash multiple layers into one with multi-stage Docker builds

const express = require('express')
const app = express()
app.get('/', (req, res) => res.send('Hello World!'))app.listen(3000, () => {
console.log(`Example app listening on port 3000!`)
})
{
"name": "hello-world",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"express": "^4.16.2"
},
"scripts": {
"start": "node index.js"
}
}
FROM node:8EXPOSE 3000WORKDIR /app
COPY package.json index.js ./
RUN npm install
CMD ["npm", "start"]
$ docker build -t node-vanilla .
$ docker run -p 3000:3000 -ti --rm --init node-vanilla
$ docker history node-vanilla
IMAGE CREATED BY SIZE
075d229d3f48 /bin/sh -c #(nop) CMD ["npm" "start"] 0B
bc8c3cc813ae /bin/sh -c npm install 2.91MB
bac31afb6f42 /bin/sh -c #(nop) COPY multi:3071ddd474429e1… 364B
500a9fbef90e /bin/sh -c #(nop) WORKDIR /app 0B
78b28027dfbf /bin/sh -c #(nop) EXPOSE 3000 0B
b87c2ad8344d /bin/sh -c #(nop) CMD ["node"] 0B
<missing> /bin/sh -c set -ex && for key in 6A010… 4.17MB
<missing> /bin/sh -c #(nop) ENV YARN_VERSION=1.3.2 0B
<missing> /bin/sh -c ARCH= && dpkgArch="$(dpkg --print… 56.9MB
<missing> /bin/sh -c #(nop) ENV NODE_VERSION=8.9.4 0B
<missing> /bin/sh -c set -ex && for key in 94AE3… 129kB
<missing> /bin/sh -c groupadd --gid 1000 node && use… 335kB
<missing> /bin/sh -c set -ex; apt-get update; apt-ge… 324MB
<missing> /bin/sh -c apt-get update && apt-get install… 123MB
<missing> /bin/sh -c set -ex; if ! command -v gpg > /… 0B
<missing> /bin/sh -c apt-get update && apt-get install… 44.6MB
<missing> /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> /bin/sh -c #(nop) ADD file:1dd78a123212328bd… 123MB
FROM node:8 as buildWORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM node:8COPY --from=build /app /
EXPOSE 3000
CMD ["index.js"]
Image for post
Image for post
$ docker build -t node-multi-stage .
$ docker history node-multi-stage
IMAGE CREATED BY SIZE
331b81a245b1 /bin/sh -c #(nop) CMD ["index.js"] 0B
bdfc932314af /bin/sh -c #(nop) EXPOSE 3000 0B
f8992f6c62a6 /bin/sh -c #(nop) COPY dir:e2b57dff89be62f77… 1.62MB
b87c2ad8344d /bin/sh -c #(nop) CMD ["node"] 0B
<missing> /bin/sh -c set -ex && for key in 6A010… 4.17MB
<missing> /bin/sh -c #(nop) ENV YARN_VERSION=1.3.2 0B
<missing> /bin/sh -c ARCH= && dpkgArch="$(dpkg --print… 56.9MB
<missing> /bin/sh -c #(nop) ENV NODE_VERSION=8.9.4 0B
<missing> /bin/sh -c set -ex && for key in 94AE3… 129kB
<missing> /bin/sh -c groupadd --gid 1000 node && use… 335kB
<missing> /bin/sh -c set -ex; apt-get update; apt-ge… 324MB
<missing> /bin/sh -c apt-get update && apt-get install… 123MB
<missing> /bin/sh -c set -ex; if ! command -v gpg > /… 0B
<missing> /bin/sh -c apt-get update && apt-get install… 44.6MB
<missing> /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> /bin/sh -c #(nop) ADD file:1dd78a123212328bd… 123MB
$ docker images | grep node-
node-multi-stage 331b81a245b1 678MB
node-vanilla 075d229d3f48 679MB

2. Remove all the unnecessary cruft from the container with distroless

FROM node:8 as buildWORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM gcr.io/distroless/nodejsCOPY --from=build /app /
EXPOSE 3000
CMD ["index.js"]
$ docker build -t node-distroless .
$ docker run -p 3000:3000 -ti --rm --init node-distroless
$ docker images | grep node-distroless
node-distroless 7b4db3b7f1e5 76.7MB
$ docker exec -ti <insert_docker_id> bash
$ docker exec -ti <insert_docker_id> node

3. Smaller base images with Alpine

FROM node:8 as buildWORKDIR /app
COPY package.json index.js ./
RUN npm install
FROM node:8-alpineCOPY --from=build /app /
EXPOSE 3000
CMD ["npm", "start"]
$ docker build -t node-alpine .
$ docker images | grep node-alpine
node-alpine aa1f85f8e724 69.7MB
$ docker run -p 3000:3000 -ti --rm --init node-alpine
Example app listening on port 3000!
$ docker exec -ti 9d8e97e307d7 bash
OCI runtime exec failed: exec failed: container_linux.go:296: starting container process caused "exec: \"bash\": executable file not found in $PATH": unknown
$ docker exec -ti 9d8e97e307d7 sh / #

What base image should you choose?

That’s all folks!

Become an expert at deploying and scaling Docker containers in Kubernetes

Skills Matter

A community of software developers with a passion for…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store