Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

Containers or virtual machines: which one to choose?

By updated

Improve this page

Logo Docker

A container is not a small machine. It is a process of the host, that is, of the machine Docker runs on. The Linux kernel, the part of the system that manages the hardware and the programs, isolates it from the rest. All containers share that same kernel.

Three things follow. A container starts like a program. It moves from one server to another with its libraries. And it does not replace a virtual machine in every case.

This lesson is for anyone who has never used Docker. You discover what a container is, you check it with a few commands, then you see how to choose between a container and a virtual machine.

What you will learn

  • Define a container as an isolated process, and not as a machine
  • Observe the same process from the host and from the container
  • See that two images of different distributions run on a single kernel
  • Explain what the portability of an image guarantees, and its limits
  • Justify the choice between a container and a virtual machine on three needs

Prerequisites

You must know what a process is on Linux: a running program, identified by a number called a PID. You also need a terminal on a Linux machine.

To replay the commands, install Docker with the guide on installing Docker. The outputs of this lesson come from Docker Engine 29.9.2. Images are referred to by their digest, a SHA-256 fingerprint that identifies an image unambiguously.

What is a container?

A container is a process started on the host. The Linux kernel shows it only a part of the system. It carries neither a kernel nor simulated hardware: it uses those of the machine that runs it. The program in the container sees its own file system, its own process numbers and its own network interface. For the host, it remains an ordinary process.

Two kernel mechanisms create this restricted view. Do not confuse them. Namespaces limit what the process sees. The namespaces(7) page of the Linux manual describes them as an abstraction that gives a group of processes the impression of having their own instance of a global resource. Cgroups (control groups) limit what the process consumes, for example memory or the number of processes. They are described in cgroups(7).

This difference helps you diagnose. If a container sees the processes of the host, the problem comes from a namespace. If it is killed because it uses too much memory, it comes from a cgroup.

Docker does not create these mechanisms. It assembles them and makes them simple to use. You can even build a container by hand (in French) with unshare and get the same result. Namespaces, cgroups and security filters are detailed in Namespaces and cgroups: measure a container's isolation. This lesson keeps only what helps you choose.

How does a container differ from a virtual machine?

A virtual machine (VM) simulates an entire machine. A hypervisor, the software that shares the hardware between several simulated machines, runs for each of them a complete system with its own kernel. A container skips that step: it starts its program directly on the kernel of the host.

The diagram shows the two stacks, layer by layer, from the server to the applications. Look at what sits under the libraries. At the top, each virtual machine has its own guest kernel, placed on the hypervisor. At the bottom, the three containers share a single kernel, that of the host system.

Two stacks of layers. At the top, the virtual machines: a server, a hypervisor, then for each of the three machines a guest kernel, libraries and an application. At the bottom, the containers: a server, a host system with a single kernel, Docker Engine, then for each of the three containers the libraries of its image and an application.Virtual machinesContainersHypervisorServerDocker EngineHost system, a single kernelServerApp AApp BApp CLibrariesLibrariesLibrariesGuest kernelGuest kernelGuest kernelApp AApp BApp CLibrariesof image ALibrariesof image BLibrariesof image C
Source · SVG
Esc to close, + and − to zoom, scroll to move around. 100 %

The table compares the two on four criteria. It gives no figures, because the differences come from the structure of the two stacks and not from a measurement.

CriterionContainerVirtual machine
What runsa process of the hosta complete system with its kernel
Kernel usedthat of the host, shareda kernel of its own for each machine
Isolation boundarykernel mechanisms (namespaces, cgroups)the hypervisor and the simulated hardware
Idle costthat of a processthat of a running system

The last two rows show up at startup. Launching a container amounts to launching a program, whereas a VM must first boot a system. On the other hand, two VMs are better separated from each other, because each has its own kernel.

How do you see the process from the host and from the container?

To convince yourself that a container is a process, look at it from both sides. You first launch a container that only waits, then you list its processes from the inside.

In the first command, -d runs the container in the background and --name gives it a name. It starts from the alpine image, a set of files that contains a small Linux distribution. sleep 600 is the program it runs: a wait of ten minutes. The second command runs ps inside the container.

Terminal window
docker run -d --name processus-observe alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a sleep 600
docker exec processus-observe ps -o pid,comm
PID COMMAND
1 sleep
7 ps

Look at the PID column. In the container, sleep has the number 1. It is the only useful program, because ps appears only because you have just launched it. The first process of a new namespace always receives the number 1. This is the role of the process namespace, described in pid_namespaces(7).

The same process also exists on the host, under another number. Ask Docker for its PID, then look for it with ps.

Terminal window
PID_HOTE=$(docker inspect --format '{{.State.Pid}}' processus-observe)
ps -o pid,ppid,comm -p "$PID_HOTE"
PID PPID COMMAND
6153 6129 sleep

This is one program with two numbers. In the container, it has the number 1. On the host, it has the number 6153 in this output.

To check that these are really two distinct spaces, compare the /proc/<pid>/ns/pid link of each process. It tells which namespace the process belongs to. The kernel lets you read this link only with administrator rights when the process belongs to another user (here root), hence sudo. The first line of the output is the space of the host, because /proc/self designates the process that is reading. The second is that of the container.

Terminal window
sudo readlink /proc/self/ns/pid "/proc/$PID_HOTE/ns/pid"
pid:[4026531836]
pid:[4026532356]

The two identifiers differ. The process of the container is therefore in a separate process space, while remaining a process of the host. If ps sees it from the host, it is no trick: a container is a process like any other.

Is the kernel really shared?

Yes. To check it, ask for the kernel version from the host, then from two containers built on two different distributions, Alpine and Debian. The --rm option removes each container as soon as its command has finished.

Terminal window
uname -r
docker run --rm alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a uname -r
docker run --rm debian:13-slim@sha256:f6b08b09a05725f9431502d3b5714f5c4576e82c1a84fa1debe0c24a7e0c3ec4 uname -r
6.12.113+deb13-amd64
6.12.113+deb13-amd64
6.12.113+deb13-amd64

The three lines give the same kernel version, that of the host. uname -r queries the kernel that runs the process, and there is only one. What changes from one container to another is everything above the kernel: the libraries and the tools provided by the image. The next two commands display the distribution name of each image.

Terminal window
docker run --rm alpine:3.24@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a grep PRETTY_NAME /etc/os-release
docker run --rm debian:13-slim@sha256:f6b08b09a05725f9431502d3b5714f5c4576e82c1a84fa1debe0c24a7e0c3ec4 grep PRETTY_NAME /etc/os-release
PRETTY_NAME="Alpine Linux v3.24"
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"

Each container presents itself as a different distribution, and yet none boots a system. "Alpine" or "Debian" here only designates the files of the image. To finish, remove the test container.

Terminal window
docker rm -f processus-observe
processus-observe

What does image portability guarantee, and where does it stop?

An image carries the libraries and files the program needs. It thus avoids failures of the "it works on my machine, but a library is missing on the server" kind. The same program, with the same files, starts the same way on a development workstation, on a test server and on a production server. The host only has to provide what the image does not contain.

The image does not contain the kernel. The container uses that of the host, whatever it is. A program that needs a feature or a module missing from that kernel therefore fails, whatever the image. A kernel flaw affects every container of the host at once. And a container cannot ask for another kernel than the one that is running.

The image also depends on the processor architecture. An image built for one type of processor does not run as is on another.

VM or container: how to choose?

The right choice depends on your need. Three common needs lead to three answers.

First need: delivering the same application, reproducibly, from the developer's workstation to production. Choose the container. The image embeds the libraries in a precise version, and launching the container amounts to starting a process. You can also run side by side applications whose dependencies are incompatible.

Second need: running another system or another kernel than that of the host, or getting stronger isolation between workloads that do not trust each other. Choose the virtual machine. It carries its own kernel, and the hypervisor separates it from the others. Only the virtual machine allows this.

Third need: running many small isolated services on the same machine, giving each a share of memory and processor. Choose the container. At idle it costs a process, and the cgroups set its limits. With one VM per service, you would have as many complete systems to maintain.

Your needRecommended choiceReason drawn from the structure
Deliver the same application everywhere, with no environment driftContainerthe image embeds its libraries, the host kernel is enough
Another operating system or a specific kernel, or strong isolation between workloads that do not trust each otherVirtual machineeach VM has its kernel and goes through the hypervisor
Many small isolated services on the same machineContainera container costs a process, the cgroups cap its resources

You can also combine the two. Containers are often run inside virtual machines, for example on the machines rented from a cloud provider. To decide, ask yourself which separation you need at that point.

Key points

  1. A container is a process of the host that the kernel isolates. It is not a machine that boots a system.
  2. Namespaces limit what the process sees, cgroups limit what it consumes.
  3. The same program has two process numbers: that of the container (1 for the program launched) and that of the host, which sees it as an ordinary process.
  4. All the containers of a host share a single kernel. uname -r gives the same version in an Alpine image, in a Debian image and on the host.
  5. An image carries the libraries and files of the program, but not the kernel. It is tied to a processor architecture.
  6. Because the kernel is shared, the container is light. For the same reason, it does not choose its kernel, and a kernel flaw affects them all.
  7. Choose the container to deliver reproducibly and to run small isolated services.
  8. Choose the virtual machine when you need another kernel or stronger isolation. You can also combine the two.

Knowledge check

Check that the essentials of this guide stuck. The questions cover only what has just been explained here.

Knowledge check

Check what you have learned with this interactive quiz

6 questions
6 min
70% to pass

How it works

  • The timer starts when you click Start
  • Multiple choice, true/false and short answer questions
  • You can move between questions
  • Detailed results are shown at the end

Starts the quiz and the timer

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn