When you’re choosing a base image for building a new container, your instinct might be to select the leanest base image possible. After all, base images that are smaller result in faster build operations. They also are more secure, in the sense that they have a smaller attack surface.
That doesn’t mean, however, that smaller is always better when selecting a base image. To prove the point, this article walks through the differences between two fundamentally distinct types of base images: Those based on Ubuntu Linux and those based on Alpine Linux.
As we’ll see, although neither base image is inherently better than the other, each is better suited to certain use cases. You shouldn’t necessarily choose Alpine over Ubuntu just because Ubuntu is a larger base image.
Table of Contents:
- Why choosing the right base image matters
- Using Ubuntu as a base image
- Working with Ubuntu base images
- Working with Alpine base images
- Ubuntu advantages vs. Alpine
- Ubuntu disadvantages vs. Alpine
- Why Ubuntu is sometimes a better base image choice than Alpine
Why choosing the right base image matters
Choosing the right base image for your container is one of the first steps in successfully containerizing an application. The type of base image you select matters due to:
- Dependencies: If the dependencies that your application requires to run are included in the base image, you don’t have to do as much work when configuring your Docker image by adding custom commands to your Dockerfile.
- Extensibility: Having more dependencies and other resources built into the base image also makes it easier to update your containers over time without having to modify Dockerfiles.
- Security: The fewer components you have in your base image, the lower the risk that the image will contain a vulnerable library or another resource that attackers could exploit.
- Image “bloat”: Using a needlessly large base image can lead to image bloat, meaning container images that are large – and therefore take longer to download – because they contain components that the application doesn’t need.
To select the right base image, then, you need to find the right balance between, on the one hand, the number of resources included in the image, and, on the other hand, the overall size of the image. These tend to be competing priorities because the more utilities and libraries you include in your base image, the larger the base image will be.
In general, base images fall into two main categories. The first is base images that are created using full-scale operating systems, like Ubuntu. The other includes images based on minimalist operating systems, like Alpine Linux.
Using Ubuntu and Alpine as examples, let’s compare each type of base image and weigh its pros and cons.
Using Ubuntu as a base image
It’s important to note that Ubuntu Base is a lighter-weight version of Ubuntu than you would get if you installed the desktop version of Ubuntu on your computer. Ubuntu Base doesn’t contain the software necessary to run a graphical interface, office software and other utilities that are irrelevant for most container use cases. So, even when you use Ubuntu as a base image, you get a relatively lean image; current Ubuntu base images are under 75 megabytes in size.
Nonetheless, if you run the Ubuntu 20.04 base image and view the contents of the /bin directory, you’ll see that it contains hundreds of utilities (and this is just /bin; there is additional software in other parts of the file system):
apt hostid script
apt-cache hostname scriptlive
apt-cdrom i386 scriptreplay
apt-config iconv sdiff
apt-get id sed
apt-key infocmp select-editor
apt-mark infotocap sensible-browser
arch install sensible-editor
awk ionice sensible-pager
b2sum ipcmk seq
base32 ipcrm setarch
base64 ipcs setpriv
basename ischroot setsid
basenc join setterm
bash kill sg
bashbug last sh
captoinfo lastb sha1sum
cat lastlog sha224sum
chage ldd sha256sum
chattr link sha384sum
chcon linux32 sha512sum
chfn linux64 shred
chgrp ln shuf
chmod locale skill
choom locale-check slabtop
chown localedef sleep
chrt logger snice
chsh login sort
cksum logname split
clear ls stat
clear_console lsattr stdbuf
cmp lsblk stty
comm lscpu su
cp lsipc sum
csplit lslocks sync
cut lslogins tabs
dash lsmem tac
date lsns tail
dd man tar
deb-systemd-helper mawk taskset
deb-systemd-invoke mcookie tee
debconf md5sum tempfile
debconf-apt-progress md5sum.textutils test
debconf-communicate mesg tic
debconf-copydb mkdir timeout
debconf-escape mkfifo tload
debconf-set-selections mknod toe
debconf-show mktemp top
delpart more touch
df mount tput
diff mountpoint tr
diff3 mv true
dir namei truncate
dircolors nawk tset
dirname newgrp tsort
dmesg nice tty
dnsdomainname nisdomainname tzselect
domainname nl uclampset
dpkg nohup umount
dpkg-deb nproc uname
dpkg-divert nsenter uncompress
dpkg-maintscript-helper numfmt unexpand
dpkg-query od uniq
dpkg-realpath pager unlink
dpkg-split partx unshare
dpkg-statoverride passwd update-alternatives
dpkg-trigger paste uptime
du pathchk users
echo perl utmpdump
egrep perl5.34.0 vdir
env pgrep vmstat
expand pidof w
expiry pidwait wall
expr pinky watch
factor pkill wc
faillog pldd wdctl
fallocate pmap whereis
false pr which
fgrep printenv which.debianutils
fincore printf who
find prlimit whoami
findmnt ps x86_64
flock ptx xargs
fmt pwd yes
fold pwdx ypdomainname
free rbash zcat
getconf readlink zcmp
getent realpath zdiff
getopt renice zdump
gpasswd reset zegrep
gpgv resizepart zfgrep
grep rev zforce
groups rgrep zgrep
gunzip rm zless
gzexe rmdir zmore
gzip run-parts znew
If you’re wondering how many files that is in all, here’s the answer:
As you can see, there are 294 utilities in the /bin directory in Ubuntu’s base image. All of this software is available in the Ubuntu base image by default, with no need on the part of developers to add it.
Working with Ubuntu base images
To use Ubuntu as a base image for your container, first create a Dockerfile that identifies Ubuntu as the image. For example:
COPY app /usr/bin/app
This Dockerfile uses Ubuntu 20.04 as its base image, and it installs an app called “app” on top of it.
If you wanted to install additional software and include it in your container, you’d set up the Dockerfile to run apt and install the desired packages. For example:
RUN apt update; apt -y install package1 package2
COPY app /usr/bin/app
To build a container from the Dockerfile, run:
Working with Alpine base images
If you opt to use an Alpine base image, you simply create a Dockerfile such as the following:
COPY app /usr/bin/app
If you need to install additional software, you can do so using apk, Alpine’s package manager:
RUN apk add –update –no-cache package1 package2
COPY app /usr/bin/app
With your Dockerfile properly configured, go ahead and build your container as per normal:
Ubuntu advantages vs. Alpine
Thanks in part to this expansive collection of built-in tools, Ubuntu base images offer certain advantages over truly minimalist base images, like Alpine:
- More tools: Ubuntu base images include more libraries and utilities by default. This reduces the amount of software that you have to install on top of the base image when building your own container image.
- Apt by default: Apt, the Ubuntu package manager, is built into the Ubuntu base images. You can use apt to install additional software from Ubuntu’s 60,000 packages when creating a container. Alpine Linux offers a package manager, too, but it does not have access to as many packages as apt.
- Widespread support: Ubuntu is one of the most popular Linux distributions, and it is widely used not just for running containers but for a variety of other purposes. This means that Ubuntu is likely to be familiar to developers and sysadmins, even if they have not used Docker before. Alpine Linux is less common outside the context of Docker.
Ubuntu disadvantages vs. Alpine
While an Ubuntu base image is advantageous in many ways, Alpine Linux can be a better choice in some situations. The disadvantages of Ubuntu as compared to Alpine include:
- Larger image size: Alpine base images total around 5.5 megabytes – much smaller than the approximately 75 megabytes that Ubuntu takes up. As a result, containers created using Alpine as a base image will also be smaller. In turn, Alpine-based containers will take less time to download, scan and (in most cases) run.
- Broader attack surface: Because Ubuntu base images include many more utilities and libraries by default than Alpine, they carry a higher security risk.
To get a sense of how much smaller Alpine is, here’s a list of the utilities include in /bin in Alpine’s base image:
That’s a total of 81 utilities in /bin – not an insignificant number, but still many fewer than Ubuntu’s 294.
Plus, if you look more closely, you’ll discover that most of Alpine’s utilities are actually just symbolic links to BusyBox, an executable that provides a number of Unix tools inside a single application:
lrwxrwxrwx 1 root root 12 Apr 4 16:06 arch -> /bin/busybox
lrwxrwxrwx 1 root root 12 Apr 4 16:06 ash -> /bin/busybox
lrwxrwxrwx 1 root root 12 Apr 4 16:06 base64 -> /bin/busybox
lrwxrwxrwx 1 root root 12 Apr 4 16:06 bbconfig -> /bin/busybox
-rwxr-xr-x 1 root root 824984 Apr 4 10:19 busybox
So, technically speaking, Alpine really contains just one utility – BusyBox. In that sense, Alpine is even leaner and easier to secure, because the only vulnerabilities you have to worry about are ones that affect BusyBox. That’s not the case in Ubuntu, where each of the hundreds of tools installed by default could be subject to any number of vulnerabilities.
The bottom line: In use cases where security is a top priority, and/or where your application has very few dependencies, it makes more sense to use Alpine as a base image than Ubuntu or a similar “full-weight” Linux distribution.
Why Ubuntu is sometimes a better base image choice than Alpine
It’s certainly the case that Alpine makes for a smaller, leaner base image than Ubuntu. That doesn’t mean, however, that Alpine is always better as a base image. In situations where your Docker containers need the additional utilities and libraries included in Ubuntu, it makes sense to opt for Ubuntu over Alpine.