Skip to content

Cookie settings

Choose which kinds of cookies you allow. You can change your choice at any time via Cookie settings at the bottom of every page. Privacy statement

Necessary

Always on

Needed to log in, keep your cart and remember your choices. These are always on.

Which cookies?
  • weboke_sessie, weboke_klant, weboke_remember: your session and staying logged in
  • weboke_basket: your cart
  • wkBtwIncl: showing prices with or without VAT
  • wok_cookies: your cookie choice, 12 months

Measures which ad leads to a visit or order, so we do not pay for ads that do nothing.

Which cookies?
  • Google Ads via Google Tag Manager (_gcl_au, _gcl_aw), up to 90 days
  • OpenAI Ads, ads in ChatGPT
  • wok_aff: which partner referred you to us (affiliate programme), 60 days

4.6

Country and language

Each country has its own site, with prices and VAT for that country.

View

Module 9 · 9.2

Installing Docker and your first container

Module 9: Docker and docker compose · Reading time: 11 minutes

Learning objective: After this lesson you can install Docker Engine from the official repository, start, list, look inside and clean up containers, and you know why the docker group is equivalent to root.

Key concepts: Docker Engine, Docker Desktop, official apt repository, Debian's docker.io, docker group, rootless mode, docker run, -d, -p, -v, --name, docker ps, docker logs, docker exec, docker stop, docker rm, docker image prune, docker system df

Which Docker?

On a server you install Docker Engine: the background service (dockerd) plus the docker command. Docker Desktop is something else: a windowed program for Windows, macOS and Linux desktops. It doesn't belong on a server.

There are two clean sources for Docker Engine:

  1. Docker's own repository (download.docker.com). Always the latest version, including the docker compose plug-in you'll use in lesson 9.3. This is what the Docker documentation describes and what this course uses.
  2. The docker.io package from Debian or Ubuntu. Older, but kept up to date by the distribution through your normal security updates (lesson 4.2). Fine if you'd rather not have anything from outside the distribution; just check that docker compose (with a space) is available.

Pick one. Mixing them causes conflicts, which is why the Docker documentation tells you to remove unofficial packages such as docker.io, docker-compose and podman-docker first.

Installing from Docker's repository

These are the steps from the Docker documentation for Debian, with the key stored separately and its own Signed-By (lesson 4.1). Take a snapshot first if that makes you feel more comfortable.

anna@web1:~$ sudo apt update
anna@web1:~$ sudo apt install ca-certificates curl
anna@web1:~$ sudo install -m 0755 -d /etc/apt/keyrings
anna@web1:~$ sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
anna@web1:~$ sudo chmod a+r /etc/apt/keyrings/docker.asc

Then the source file /etc/apt/sources.list.d/docker.sources (deb822 format):

Types: deb
URIs: https://download.docker.com/linux/debian
Suites: trixie
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc

For Suites you fill in the codename of your release (cat /etc/os-release, lesson 4.3) and for Architectures the output of dpkg --print-architecture. On Ubuntu the URL is https://download.docker.com/linux/ubuntu and the suite is, for example, noble.

Then install:

anna@web1:~$ sudo apt update
anna@web1:~$ sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
anna@web1:~$ sudo docker run hello-world
...
Hello from Docker!
This message shows that your installation appears to be working correctly.

On Debian and Ubuntu, Docker starts automatically after installation and after every reboot (systemctl status docker, lesson 6.1).

Note: by default, unattended-upgrades (lesson 4.2) does not update packages from this repository automatically. Include sudo apt update && sudo apt upgrade for Docker in your maintenance routine (lesson 10.4), or deliberately add the repository to your automatic updates.

The docker group is root

All over the internet you'll read that you should add yourself to the docker group so you no longer have to type sudo:

anna@web1:~$ sudo usermod -aG docker anna

Only do that if you understand what it means. The Docker documentation is clear about it: the docker group grants root-level privileges. Anyone who can control the Docker service can start a container with the host's entire disk mounted inside it:

docker run -it -v /:/host debian chroot /host

and is then root on your server, without a password and without a sudo log entry. As the Docker documentation puts it: only trusted users should be allowed to control the Docker service.

For a server you manage on your own, it's a trade-off: convenience versus an extra route to root. Safer is to simply keep typing sudo docker ...; then every command ends up in the sudo log (lesson 3.2). In any case, never add a user to the group who shouldn't be root, such as a service user for a website.

Rootless mode

Docker can also run without root: the service and the containers then run under an ordinary user, with their own user namespace. You install it as that user with dockerd-rootless-setuptool.sh install (from the docker-ce-rootless-extras package; the uidmap package is needed beforehand). A breach in a container then doesn't give anyone root on the host. There are limitations and it's harder to set up; in this course we use the regular installation and keep it safe with the rules from this module.

A first real container

We'll start an nginx web server in a container, reachable only from the server itself:

anna@web1:~$ sudo docker run -d --name proef -p 127.0.0.1:8080:80 nginx:stable
Unable to find image 'nginx:stable' locally
stable: Pulling from library/nginx
...
3f1c9e0d7a2b...

The parts:

PartMeaning
-dRun in the background (detached)
--name proefA name, so you don't have to work with long IDs
-p 127.0.0.1:8080:80Port 80 in the container becomes port 8080 on the host, on loopback only
nginx:stableThe official nginx image, tag stable

Check it:

anna@web1:~$ sudo docker ps
CONTAINER ID   IMAGE          COMMAND                  STATUS         PORTS                    NAMES
3f1c9e0d7a2b   nginx:stable   "/docker-entrypoint.…"   Up 5 seconds   127.0.0.1:8080->80/tcp   proef
anna@web1:~$ curl -I http://127.0.0.1:8080
HTTP/1.1 200 OK
Server: nginx/1.28.0
anna@web1:~$ sudo ss -tlnp | grep 8080
LISTEN 0 4096 127.0.0.1:8080 0.0.0.0:* users:(("docker-proxy",pid=2211,fd=7))

ss (lesson 5.1) confirms it: only on 127.0.0.1. Had you typed -p 8080:80, you would have seen 0.0.0.0:8080 and [::]:8080, and your container would be reachable from the internet, even if ufw doesn't allow port 8080 (lesson 9.3). The version in the Server header may differ on your server.

Looking around and stepping in

anna@web1:~$ sudo docker logs proef            # what the program wrote (stdout and stderr)
anna@web1:~$ sudo docker logs -f --tail 20 proef   # last 20 lines and keep following
anna@web1:~$ sudo docker exec -it proef sh     # a shell in the running container
# cat /etc/os-release
# exit
anna@web1:~$ sudo docker stop proef
anna@web1:~$ sudo docker start proef
anna@web1:~$ sudo docker rm -f proef           # stop and remove

docker logs is the first place to look when a container doesn't do what you expect, just like journalctl for ordinary services (lesson 6.2). With docker exec you look inside, but it's best not to change anything there: changes inside the container disappear with the next update (lesson 9.1).

Cleaning up

Images and stopped containers stay around until you clean them up. On a VPS with a small disk, that quietly adds up.

anna@web1:~$ sudo docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          7         3         1.9GB     1.2GB (63%)
Containers      3         3         12MB      0B (0%)
Local Volumes   2         2         180MB     0B (0%)
Build Cache     0         0         0B        0B
anna@web1:~$ sudo docker image prune

docker image prune removes unnamed images that no longer belong to anything; with -a it also removes all images not used by any container. Be careful with docker system prune --volumes: it also removes anonymous volumes (without a name) that aren't attached to a container at that moment, and docker volume prune -a even removes named volumes. Those may hold your data. So always give volumes a name or use a bind mount (lesson 9.1), and check first with docker volume ls.

Container logs can grow too. By default Docker stores them in JSON files with no size limit. You set a limit in /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}

Then run sudo systemctl restart docker; the setting only applies to newly created containers, so recreate existing ones.

In practice

Docker works on a WebOké VPS the same way as with any other provider: it's software inside your own server that you install and update yourself. On a small VPS, check your disk space regularly with docker system df and df -h (lesson 1.4).

Summary

  • Install Docker Engine from Docker's repository (with its own key and Signed-By) or docker.io from the distribution, not both.
  • The docker group is equivalent to root; sudo docker leaves a trail in the sudo log. Rootless mode is a safer but more limited alternative.
  • docker run -d --name ... -p 127.0.0.1:host:container image:tag starts a container that is only reachable locally; check with ss.
  • docker ps, logs, exec, stop, rm are your everyday tools.
  • Clean up images with docker image prune, limit container logs (unlimited by default), and be sparing with --volumes.

Quiz

1. Why does membership of the docker group effectively give root privileges?



2. After docker run -d -p 8080:80 nginx, ss -tlnp shows the line 0.0.0.0:8080. What should you have typed to publish the port locally only?



3. A container starts, but the website inside it doesn't work. What do you look at first?



4. Why do you pick one source for Docker instead of mixing docker.io and docker-ce?



5. What does docker system prune --volumes do that you really need to know?



Exercise

Exercise O9.2: Your first container. Install Docker Engine on your server as described in this lesson (or on a test machine). Start an nginx container that listens only on 127.0.0.1:8080, check it with docker ps, curl -I and ss -tlnp, look at the logs, and then clean it up. Answer these questions: which source did you install Docker from, what did ss show for port 8080, and did you add yourself to the docker group (why or why not)?

Exercise O9.2: Your first container

You do this exercise yourself; no one checks it. Tick off what you've done and mark the exercise as done.




Your ticks and notes only stay in this browser; we don't send them anywhere. If you're signed in, we only save in My courses that you've done the exercise.

Further reading

One moment…

Set up your domains

For each domain, arrange the nameservers, the link to your hosting and who becomes the owner.

Loading your domains…