How to minimize your Docker image. 5 tips and tricks. Example of minifying from 328MB to 56MB.

You can be surprised how much disk space Docker images can consume. In this article we will look at several ways of how to minimize your Docker image. We will build an image with Dante socks server as an example. Dante will not be installed from binary packages, but will be compiled by ourselves inside Docker image.

Beginning. Naive solution. Image size: 328MB.

Let’s create simple Dante config file danted.conf.

internal: 0.0.0.0 port = 1080
internal: :: port = 1080
external: eth0
errorlog: stderr
logoutput: stdout

socksmethod: username

user.privileged: root
user.unprivileged: nobody

client pass {
        from: 0/0 to: 0/0
        session.max: 200
        log: error connect disconnect
}

socks pass {
        from: 0/0 to: 0/0
        log: error connect disconnect
}

Next, create Dockerfile.

FROM debian:stable
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y wget gcc make
WORKDIR /root/
RUN wget ftp://www.inet.no/dante/files/dante-1.4.2.tar.gz
RUN tar zxvf dante-1.4.2.tar.gz
WORKDIR /root/dante-1.4.2
RUN ./configure
RUN make
RUN make install
COPY danted.conf /etc/
RUN useradd testuser
RUN echo "testuser:pass123pass" | chpasswd
RUN echo "precedence ::ffff:0:0/96  100" >> /etc/gai.conf
EXPOSE 1080
CMD sockd -f /etc/danted.conf

Building image… God, it weights 328M.

sudo docker build -t localhost/debian-danted:testing .
sudo docker save -o danted.tar localhost/debian-danted:testing
du -h danted.tar
#328M    danted.tar

Let’s minify it.

Tip #1. Use a smaller base image. Image size: 260MB.

If your app not depends on OS, consider using alpine linux image. This is very compact and popular OS for Docker base image.
If your app depends on OS, for example Debian, try to use more compact debian:stable-slim image.
Let’s change our base image to debian:stable-slim.

FROM debian:stable-slim
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y wget gcc make
WORKDIR /root/
RUN wget ftp://www.inet.no/dante/files/dante-1.4.2.tar.gz
RUN tar zxvf dante-1.4.2.tar.gz
WORKDIR /root/dante-1.4.2
RUN ./configure
RUN make
RUN make install
COPY danted.conf /etc/
RUN useradd testuser
RUN echo "testuser:pass123pass" | chpasswd
RUN echo "precedence ::ffff:0:0/96  100" >> /etc/gai.conf
EXPOSE 1080
CMD sockd -f /etc/danted.conf
sudo docker build -t localhost/debian-danted:testing .
sudo docker save -o danted.tar localhost/debian-danted:testing
du -h danted.tar
#260M    danted.tar

A way better.

Tip #2. Don’t install recommended packages. Image size: 253MB.

Use apt with –no-install-recommends option. Install missing packages manually.

FROM debian:stable-slim
RUN apt-get update
RUN apt-get upgrade -y
RUN apt-get install -y --no-install-recommends wget gcc make libc-dev
WORKDIR /root/
RUN wget ftp://ftp.inet.no/pub/socks/dante-1.4.2.tar.gz
RUN tar zxvf dante-1.4.2.tar.gz
WORKDIR /root/dante-1.4.2
RUN ./configure
RUN make
RUN make install
COPY danted.conf /etc/
RUN useradd testuser
RUN echo "testuser:pass123pass" | chpasswd
RUN echo "precedence ::ffff:0:0/96  100" >> /etc/gai.conf
EXPOSE 1080
CMD sockd -f /etc/danted.conf
sudo docker build -t localhost/debian-danted:testing .
sudo docker save -o danted.tar localhost/debian-danted:testing
du -h danted.tar
#253M    danted.tar

Tip #3. Remove unused files and packages, but do it in the same Docker layer. Image size: 121MB.

By default, Docker creating a layer for each Dockerfile line. Layer consists of relative to the previous line or relative to base image (for the first line) changes. Files, removed in current layer, will not lower Docker image size – previous layer containing removed files stays unchanged.
Docker will not rebuild image from beginning if you will change only the last line of your Dockerfile, but this feature can significantly increase the image size.
Let’s move compilation, installation and removing into the one layer.

FROM debian:stable-slim
RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends wget gcc make libc-dev && \
	wget ftp://ftp.inet.no/pub/socks/dante-1.4.2.tar.gz -P /root && cd /root && tar zxvf dante-1.4.2.tar.gz && \
	cd /root/dante-1.4.2 && ./configure && make && make install && \
	rm -r /root/dante* && apt-get -y remove wget gcc make libc-dev && apt-get -y autoremove --purge && apt-get -y clean
COPY danted.conf /etc/
RUN useradd testuser && echo "testuser:pass123pass" | chpasswd
RUN echo "precedence ::ffff:0:0/96  100" >> /etc/gai.conf
EXPOSE 1080
CMD sockd -f /etc/danted.conf
sudo docker build -t localhost/debian-danted:testing .
sudo docker save -o danted.tar localhost/debian-danted:testing
du -h danted.tar
#121M    danted.tar

Tip #4. Experimental squash build option. Image size: 120MB.

After building, Docker will take all the layers and collapse them into a single new layer. All intermediate layers can be removed. This collapsing can save a little bit of disk space.
Let’s change Docker config /etc/docker/daemon.json to allow experimental options.

{
  "experimental": true,
}

Restart Docker.

sudo systemctl restart docker

Rebuild image with squash option.

sudo docker build --squash -t localhost/debian-danted:testing .
sudo docker save -o danted.tar localhost/debian-danted:testing
du -h danted.tar
#120M    danted.tar

Tip #5. Using btrfs filesystem level compression. Image size: 56MB.

OK. We did all to minimize image size to transfer it through network. But what if we want to store a lot of images locally? Let’s try to use btrfs compression. We will create btrfs volume over a file for this example.

sudo apt install btrfs-progs
sudo fallocate -l 2G /root/btrfs.tmp
sudo mkfs.btrfs /root/btrfs.tmp
sudo mount /root/btrfs.tmp /var/lib/docker -o compress-force=zstd

Let’s change Docker config /etc/docker/daemon.json to allow btrfs storage-driver.

{
  "experimental": true,
  "storage-driver": "btrfs"
}

Restart Docker, build image, remove unused dependencies.

sudo systemctl restart docker
sudo docker build --squash -t localhost/debian-danted:testing .
sudo docker run --restart=always -it -d --name danted localhost/debian-danted:testing
sudo docker image prune -a
sudo docker image rm debian:stable-slim
sudo apt install btrfs-compsize
sudo compsize /var/lib/docker/
Processed 7481 files, 2121 regular extents (3468 refs), 4906 inline.
Type       Perc     Disk Usage   Uncompressed Referenced
TOTAL       46%       56M         121M         192M
none       100%      643K         643K         879K
zstd        46%       56M         120M         191M

56MB. Very nice.

Check.

#sudo docker run --restart=always -it -d --name danted localhost/debian-danted:testing
curl --proxy socks5://testuser:pass123pass@172.17.0.2:1080 innerlife.io

Here you should see my site content, which means Dante container is running and working.

1 thought on “How to minimize your Docker image. 5 tips and tricks. Example of minifying from 328MB to 56MB.”

Leave a Comment

Your email address will not be published. Required fields are marked *