Linux Sysadmin · Lesson 16 · 25 min

Alpine Linux: tiny Linux for containers

Alpine is the Linux you've probably already run without knowing it. It's inside a huge number of containers, because its whole base system is about 8 MB (Ubuntu's container image is about 80). It also runs on routers, Raspberry Pis and small cloud servers. Alpine is small because it does almost everything its own way, so let's meet it properly, the way most people do: inside a container.

You will learn

  • Running an Alpine container with podman (and docker)
  • apk, Alpine's package manager
  • What makes Alpine different: BusyBox, the ash shell, musl, OpenRC, and no sudo
  • When Alpine is a great choice, and when it isn't

Why so small?

Fewer programs also means fewer security bugs to patch, which is one reason people build containers on it. A new Alpine release comes out every six months (May and November), and each one gets about two years of updates.

Run Alpine in a container

A container runs a program with its own private set of files (here, a whole mini Alpine system), while sharing the host computer's kernel. It starts in a second, and you can throw it away when you're done. podman runs containers as your normal user, with no root needed. Docker does the same job and uses the same commands, but its background service runs as root.

Rocky / RHEL
sudo dnf install podman
podman run -it docker.io/library/alpine:3.22

podman is Red Hat's tool. Docker isn't in Rocky's repos: install podman-docker if you want a “docker” command that runs podman.

Ubuntu / Debian
sudo apt install podman
podman run -it docker.io/library/alpine:3.22
# or Docker:
sudo apt install docker.io
sudo docker run -it alpine:3.22

Docker needs sudo (or the docker group, which is as powerful as root).

-it means “interactive, with a terminal”, and alpine:3.22 is the image name and version (tag). The full name docker.io/library/alpine says exactly which registry to download from, and it works everywhere. The prompt changes to / #: you're root, in /, inside Alpine. Type exit to leave.

/ # cat /etc/alpine-release
3.22.1
/ # uname -r
5.14.0-570.22.1.el9_6.x86_64     ← the HOST's kernel: containers share it

apk: Alpine's package manager

Inside Alpine
apk update              # refresh the package list
apk add bash curl       # install
apk del bash            # remove
apk search nginx        # find packages
apk info                # what's installed
apk upgrade             # update everything
cat /etc/apk/repositories   # main + community
(1/4) Installing ncurses-terminfo-base (6.5_p20250503-r0)
(2/4) Installing libncursesw (6.5_p20250503-r0)
(3/4) Installing readline (8.2.13-r1)
(4/4) Installing bash (5.2.37-r0)
OK: 9 MiB in 19 packages

In container recipes (Dockerfiles) you'll see apk add --no-cache PKG. It installs without keeping the downloaded package list, so the image stays tiny.

How Alpine is different

AlpineRockyUbuntu
Install softwareapk adddnf installapt install
Everyday toolsBusyBox (fewer options)GNU coreutilsGNU coreutils
Shellash (/bin/sh); bash is optionalbashbash (sh = dash)
C librarymuslglibcglibc
ServicesOpenRC: rc-service, rc-updatesystemdsystemd
Become rootsu, or add doas / sudosudosudo
Add a useradduser -D alice (BusyBox)useraddadduser
Logs/var/log/messagesjournalctljournalctl

BusyBox: one program, many names

ls -l /bin/ls shows /bin/ls -> /bin/busybox. Almost every command is a link to the same program. BusyBox versions are smaller and have fewer options than the GNU ones, so a script that uses grep -P or date -d "next friday" may fail. apk add coreutils grep installs the full GNU versions if you need them.

ash, not bash

Alpine's /bin/sh is BusyBox ash. It's like Ubuntu's dash (lesson 12) but a bit friendlier: it understands [[ ]] and source. It doesn't do <( ), arrays or {1..5}. That's why the lesson 14 advice pays off: a POSIX #!/bin/sh script runs on Alpine unchanged. Need bash? apk add bash, then bash, and the prompt becomes bash-5.2#.

musl: the “it's right there, but not found” error

A program downloaded for “Linux” is usually built for glibc. On Alpine it can fail with a baffling message:

/ # ./mytool
sh: ./mytool: not found              ← but ls shows it's right there!

It isn't mytool that's missing. It's the glibc loader it needs (/lib64/ld-linux-x86-64.so.2). The fixes: use the program's Alpine or musl build, install gcompat (a compatibility layer that sometimes works), or pick a glibc-based image such as debian or almalinux for that app. ldd --version tells you which C library you have.

OpenRC, and why containers don't use it

On a real Alpine server (VM, Raspberry Pi)
apk add nginx
rc-service nginx start          # like: systemctl start nginx
rc-update add nginx default     # like: systemctl enable nginx
rc-status                       # like: systemctl list-units

Inside a container there is no init system at all. The container runs one program (here, the shell), and when it exits, the container stops. Run rc-service in a container and OpenRC warns you that it “did not boot” this system. Container tools start the app directly instead.

Containers come and go

On the host (Rocky or Ubuntu)
podman ps -a                   # all containers, including stopped ones
podman images                  # downloaded images (the templates)
podman run --rm docker.io/library/alpine:3.22 cat /etc/alpine-release   # one command, then delete it
podman rm NAME                 # delete a stopped container

An image is the template and a container is one running copy of it. Changes you make inside a container (like apk add bash) stay in that container until you remove it. A fresh podman run starts clean from the image again. Real projects write the setup steps into a recipe file, called a Containerfile or Dockerfile, so every container starts the same way. A future Containers path will cover that.

Should I use Alpine?

Try it: a trip into Alpine 🏔️

You'll start on the Rocky or Ubuntu server, launch an Alpine container, look around, install bash, and come back.

Quick check

1. Inside an Alpine container on a Rocky server, uname -r shows 5.14.0-…el9_6. Why?

2. bash script.sh in an Alpine container says sh: bash: not found. The quickest fix?

3. A program you downloaded is definitely in the folder, but Alpine says ./mytool: not found. What's likely going on?

4. What's the Alpine equivalent of sudo systemctl enable --now nginx on a real (non-container) Alpine server?

Finished the missions and the quiz? Mark it done to track your progress.

Next up: Git · Track & share your work, starting with “Git: save points for your work”.