Why Use Ubuntu as a Docker Base Image When Alpine Exists?

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

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

Ubuntu base images, which you can find on Docker Hub, are created using the root file system of Ubuntu Base, a variant of the Ubuntu Linux operating system.

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):

# ls /bin
‘[‘               hardlink          runcon
 addpart           head              savelog
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:

# ls /bin | wc -l
294

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:

FROM ubuntu:20.04
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:

FROM ubuntu:20.04
RUN apt update; apt -y install package1 package2
COPY app /usr/bin/app

To build a container from the Dockerfile, run:

docker build -t container_name

Working with Alpine base images

If you opt to use an Alpine base image, you simply create a Dockerfile such as the following:

FROM alpine
COPY app /usr/bin/app

If you need to install additional software, you can do so using apk, Alpine’s package manager:

FROM alpine
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:

docker build -t container_name

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:

# ls -1 /bin
arch
ash
base64
bbconfig
busybox
cat
chgrp
chmod
chown
cp
date
dd
df
dmesg
dnsdomainname
dumpkmap
echo
ed
egrep
false
fatattr
fdflush
fgrep
fsync
getopt
grep
gunzip
gzip
hostname
ionice
iostat
ipcalc
kbd_mode
kill
link
linux32
linux64
ln
login
ls
lzop
makemime
mkdir
mknod
mktemp
more
mount
mountpoint
mpstat
mv
netstat
nice
pidof
ping
ping6
pipe_progress
printenv
ps
pwd
reformime
rev
rm
rmdir
run-parts
sed
setpriv
setserial
sh
sleep
stat
stty
su
sync
tar
touch
true
umount
uname
usleep
watch
zcat

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:

# ls -l /bin
total 808
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.