Virtual machines (VMs) let you run individual operating systems (OSes) on top of the same bare-metal machine to provide environment isolation. Each VM has its own kernel and all the processes running in that VM are fully isolated from the processes running in other VMs. VMs execute CPU instructions through hypervisors.

Containers use two Linux features called namespaces and cgroups to achieve environment isolation. Processes running in different namespaces share the same host OS kernel but they cannot see each other, as if they are running in isolated environments. There are 7 kinds of namespaces and each type isolates a certain aspect of the environment. For example, processes running in different network namespaces see different network interfaces. Cgroups are used to set the resource limit that a process or a group of processes can use. Container technologies make it possible to run disparate microservices on the same host OS without performing complex configuration, which otherwise would require different hosting VMs or physical machines.

Compared to containers, VMs use more resources but provide true environment isolation. Containers are much more lightweight alternatives that require less configuration effort and are fast to create. Container-based platforms, such as Docker, require the same type of kernel to run a given image, especially when a container is compiled with specific kernel modules. For example, an image created with Docker on x86 will not run on ARM CPUs even though Docker does support ARM architecture. VMs don’t have such restrictions.