debian, docker, linux

Creating a minimal Debian container for Docker

In the last post, we introduced some basic techniques to free up unused space on a Debian system. Following those steps, I created a Debian 8 Docker image that takes only 56.7 MB!

Usage

You can get it typing the following, but you really don’t need to because docker run  pulls the image for you if you do not already have it. It is still useful to get updates.

docker pull ownyourbits/minidebian

Bash into it with

docker run --rm -ti ownyourbits/minidebian

Run any command inside the container, for instance list root with

docker run --rm -ti ownyourbits/minidebian ls /
Is this small?

In order to see how small this really is, we can compare it to a minimal system built using debootstrap. More details on this below.

Docker images can be made very small, because there are parts of the system that are not needed in order to launch things in a container. If we take a look at the official Debian Docker repository, we can see that their images are smaller than the file system generated by bootstrap. They even have slim versions that get rid of locales and man pages, but they are still bigger than ownyourbits/minidebian.

$ docker images
REPOSITORY             TAG           IMAGE ID      CREATED       SIZE
ownyourbits/minidebian latest        e3d72e6d0731  40 hours ago  56.7 MB
debootstrap-sid        latest        a702df4074d3  41 hours ago  316 MB
debian                 jessie-slim   7d86024f45a4  4 weeks ago   79.9 MB
debian                 jessie        e5599115b6a6  4 weeks ago   123 MB
Is this useful for anything?

It is! docker containers are quite handy to use and allow us to play around with a Debian system easily. Sure, we always could do this with a virtual machine or with bootstrap, but there are benefits to using docker.

One benefit lays in the fact that Docker uses overlayfs, so any changes made to your container will be lost when you exit, unless you issue docker commit. We can play around, we can experiment, break things without fear, and then throw it away.

Another benefit is that we can use it to build more complex systems, overlaying a database, Java runtime, or a web server on top of it. That means that if an Apache server adds a 140 MB layout, you only have to get that compressed overlay, which is quite fast and space efficient.

It is also convenient to distribute stuff with dependencies. Everything is packed for you and you do not have to deal with configuration. This makes trying things out easy. Want to get a feel of gentoo? docker pull gentoo/stage3-amd64 will save you tons of compilation and configuration time.

Finally, we can share this easily on dockerhub.io or our private docker repo.

Details

In order to get a working Debian system that we can then trim down, we have different options.

One of them is working on a live ISO, another is starting from the official Debian Docker repo that we mentioned earlier.

Another one is using good old debootstrap. Debootstrap is a little tool that gets the base debs from the official repositories, then installs them in a directory, so you can chroot to it. It provides the basic directory structure for Debian.

We can see what packages Debian considers essential

$ debootstrap --print-debs sid .
I: Keyring file not available at /usr/share/keyrings/debian-archive-keyring.gpg; switching to https mirror https://deb.debian.org/debian
I: Retrieving InRelease
I: Retrieving Packages
I: Validating Packages
I: Resolving dependencies of required packages...
I: Resolving dependencies of base packages...
I: Found additional required dependencies: libaudit-common libaudit1 libbz2-1.0 libcap-ng0 libdb5.3 libdebconfclient0 libgcrypt20 libgpg-error0 liblz4-1 libncursesw5 libsemanage-common libsemanage1 libsystemd0 libudev1 libustr-1.0-1
I: Found additional base dependencies: dmsetup gnupg-agent libapparmor1 libassuan0 libbsd0 libcap2 libcryptsetup4 libcurl3-gnutls libdevmapper1.02.1 libdns-export162 libelf1 libfastjson4 libffi6 libgmp10 libgnutls30 libgssapi-krb5-2 libhogweed4 libidn11 libidn2-0 libip4tc0 libip6tc0 libiptc0 libisc-export160 libk5crypto3 libkeyutils1 libkrb5-3 libkrb5support0 libksba8 libldap-2.4-2 libldap-common liblocale-gettext-perl liblognorm5 libmnl0 libnetfilter-conntrack3 libnettle6 libnfnetlink0 libnghttp2-14 libnpth0 libp11-kit0 libperl5.24 libpsl5 librtmp1 libsasl2-2 libsasl2-modules-db libseccomp2 libsqlite3-0 libssh2-1 libtasn1-6 libtext-charwidth-perl libtext-iconv-perl libtext-wrapi18n-perl libunistring0 libxtables12 openssl perl perl-modules-5.24 pinentry-curses xxd
base-files base-passwd bash bsdutils coreutils dash debconf debianutils diffutils dpkg e2fslibs e2fsprogs findutils gcc-5-base gcc-6-base grep gzip hostname init-system-helpers libacl1 libattr1 libaudit-common libaudit1 libblkid1 libbz2-1.0 libc-bin libc6 libcap-ng0 libcomerr2 libdb5.3 libdebconfclient0 libfdisk1 libgcc1 libgcrypt20 libgpg-error0 liblz4-1 liblzma5 libmount1 libncurses5 libncursesw5 libpam-modules libpam-modules-bin libpam-runtime libpam0g libpcre3 libselinux1 libsemanage-common libsemanage1 libsepol1 libsmartcols1 libss2 libsystemd0 libtinfo5 libudev1 libustr-1.0-1 libuuid1 login lsb-base mawk mount multiarch-support ncurses-base ncurses-bin passwd perl-base sed sensible-utils sysvinit-utils tar tzdata util-linux zlib1g adduser apt apt-transport-https apt-utils blends-tasks bsdmainutils ca-certificates cpio cron debconf-i18n debian-archive-keyring dmidecode dmsetup gnupg gnupg-agent gpgv ifupdown init iproute2 iptables iputils-ping isc-dhcp-client isc-dhcp-common kmod libapparmor1 libapt-inst2.0 libapt-pkg5.0 libassuan0 libbsd0 libcap2 libcryptsetup4 libcurl3-gnutls libdevmapper1.02.1 libdns-export162 libelf1 libestr0 libfastjson4 libffi6 libgdbm3 libgmp10 libgnutls30 libgssapi-krb5-2 libhogweed4 libidn11 libidn2-0 libip4tc0 libip6tc0 libiptc0 libisc-export160 libk5crypto3 libkeyutils1 libkmod2 libkrb5-3 libkrb5support0 libksba8 libldap-2.4-2 libldap-common liblocale-gettext-perl liblogging-stdlog0 liblognorm5 libmnl0 libnetfilter-conntrack3 libnettle6 libnewt0.52 libnfnetlink0 libnghttp2-14 libnpth0 libp11-kit0 libperl5.24 libpipeline1 libpopt0 libprocps6 libpsl5 libreadline6 libreadline7 librtmp1 libsasl2-2 libsasl2-modules-db libseccomp2 libslang2 libsqlite3-0 libssh2-1 libssl1.0.2 libssl1.1 libstdc++6 libtasn1-6 libtext-charwidth-perl libtext-iconv-perl libtext-wrapi18n-perl libunistring0 libusb-0.1-4 libxapian30 libxtables12 logrotate nano netbase openssl perl perl-modules-5.24 pinentry-curses procps readline-common rsyslog systemd systemd-sysv tasksel tasksel-data udev vim-common vim-tiny wget whiptail xxd

This is what debootstrap considers a base filesystem. We already see things that will not be needed in a container. Let’s create the filesystem.

mkdir debian_root && cd debian_root
sudo debootstrap sid .

We can then chroot to it manually. Some preparations need to be done to interface the new userspace with the virtual filesystems it expects.

sudo mount -t devpts devpts debian_root/dev/pts
sudo mount -t proc proc debian_root/proc
sudo mount -t sysfs sysfs debian_root/sys
sudo chroot debian_root

That is already more cumbersome than using Docker. Docker also offers more advanced isolation using newer kernel features like namespaces and cgroups.

It is easier to import this filesystem as a Docker image.

tar -c * | docker import - minidebian:raw

Now we can start freeing up space. The problem with this is that, because Docker uses overlays, you will not get a smaller container even if you delete things. This happens because when you delete in an upper layer it is just marked as deleted so that you can go back to the original contents just by getting rid of the upper layer.

In order to get around this, we can repack everything in an unique layer with

docker container create --name minidebian-container minidebian
docker export minidebian-container | docker import - minidebian:raw

When we are happy with the result, we end up with a Docker image with no metadata. We are only left with creating a basic dockerfile in an empty directory

FROM minidebian:raw

LABEL description="Minimal Debian 8 image"
MAINTAINER Ignacio Núñez Hernanz <nacho@ownyourbits.com>

CMD ["/bin/bash"]

, and building the final image

docker build . -t minidebian:latest

In this example, we have only indicated Docker to spawn bash  if no other arguments are given.

In the next post we will create a LAMP installation on top of this small debian layer.

Author: nachoparker

Humbly sharing things that I find useful [ github dockerhub ]

6 Comments on “Creating a minimal Debian container for Docker

  1. Thanks for your article, very interesting.

    Nowadays the official debian:stretch-slim image is 55.3MB in size. Could that one be minified even further?

    1. Yes, fortunatedly this situation has improved a lot and now the official debian images are pretty small. I don’t use my minified version anymore, so I can save the extra work of updating the base container.

  2. One thing I wasn’t clear on. after the chroot, do I ‘exit’ the chroot? and then run the tar command or do I run the WHILE in another window while chroot is running? Not clear on that.

  3. What if you want to create a full blown Debian image? For example, I have a Linux box running CentOS 8, but I want to mess around with Kali Linux, which is Debian based. How would install more stuff into a Debian image? I am just now learning Docker, so will probably figure it out eventually, but any help appreciated.

Leave a Reply

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