Optimize Docker images by squashing layers
When Docker builds an image, it utilizes a layered filesystem (AUFS). Each command in the Dockerfile adds a new layer that contains only the changes from the previous layer. This makes builds very fast since only changed files have to be copied.
However, the layered filesystem also results in larger image sizes since each layer contains duplicate files. Squashing combines these layers into a single layer, reducing storage space and often improving runtime performance by decreasing mount points.
Docker provides an experimental --squash
option for building optimized image by squashing its layers into one, reducing image size. However, it also discards all original image’s instructions like ENV, LABEL, etc.
So I decided to try making a simple shell script that also can squash image layers but keeps ENV, LABEL and other instructions from the original image.
The shell script called ‘docker-squash.sh’ (https://github.com/shinsenter/docker-squash).
Download
You can download the docker-squash.sh script from this GitHub repository and save it to your local file system.
Alternatively, you can run these commands with superuser (root) privileges to download the script to the system directory /usr/local/bin using curl:
sudo curl -sL https://github.com/shinsenter/docker-squash/raw/main/docker-squash.sh -o /usr/local/bin/docker-squash.sh
sudo chmod +x /usr/local/bin/docker-squash.sh
Usage
docker-squash.sh <source_image> [docker build options]
Where:
source_image
- The original image ID orname:tag
to be squasheddocker build options
- Additional build options like--build-arg
,--label
, etc.
Use-cases
Squashing existing images or Dockerfiles is seamless while providing size and performance benefits.
Squash an existing image to reduce its size:
docker pull php:apache
docker-squash.sh php:apache -t php:apache-squashed
docker images | grep -F 'apache'
# Original image size: 515MB
# Squashed image size: 509MB
Inject pre-squash script which is executed before squashing:
You can add your shell script to a build argument named PRESQUASH_SCRIPTS
and execute as root user. It may be useful to clean up the source image before squashing.
docker pull php:apache
docker-squash.sh php:apache \
--build-arg PRESQUASH_SCRIPTS='apt-get -y autoremove --purge autoconf dpkg-dev file g++ gcc libc-dev make pkg-config re2c *-dev' \
-t php:apache-squashed
docker images | grep -F 'apache'
# Original image size: 515MB
# Squashed image size: 275MB
Specify target platform to squash multi-architecture images:
docker pull --platform linux/arm64 ubuntu:jammy
docker-squash.sh ubuntu:jammy -t ubuntu:jammy-squashed --platform linux/arm64
docker images | grep -F 'jammy'
Squash image built straight from a Dockerfile:
docker-squash.sh /home/myproject/Dockerfile -t myproject:squashed
Pros and Cons
Pros
- Smaller image size
- Faster runtime performance
- Obfuscates build details
- Retains Dockerfile metadata
Cons
- Slower rebuild time
- Can inhibit debugging capabilities
- Loss of Docker cache and remote build benefits
Conclusion
In other words — you can make smaller Docker images but preserving more of the original image definition!
Have fun!