Docker Introduction
What containers are, how they differ from VMs, installing Docker, and your first container.
The problem containers solve
"It works on my machine" is the problem Docker exists to kill. An application typically depends on a specific runtime version, specific system libraries, specific environment variables, and specific configuration files — and getting all of that identical across a developer's laptop, a CI runner, and a production server has historically been a constant source of bugs that have nothing to do with the actual application code.
Containers package an application together with everything it needs to run — code, runtime, system libraries, configuration — into a single, portable unit that behaves identically wherever it runs.
Containers vs. virtual machines
Both containers and VMs solve "isolate this workload from that one," but at completely different levels of the stack.
A virtual machine virtualizes hardware: a hypervisor lets multiple VMs run on one physical machine, and each VM runs its own complete guest operating system — its own kernel, its own drivers, everything — as if it were a separate physical computer.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ App A │ │ App B │ │ App C │
│ Guest OS │ │ Guest OS │ │ Guest OS │ <- each VM: full OS + kernel
├─────────────┴──┴─────────────┴──┴─────────────┤
│ Hypervisor │
├─────────────────────────────────────────────────┤
│ Host Operating System │
├─────────────────────────────────────────────────┤
│ Hardware │
└─────────────────────────────────────────────────┘
A container virtualizes at the operating system level instead: all containers on one machine share the same host kernel, and each container just gets an isolated view of processes, network, and filesystem via kernel features (namespaces and cgroups on Linux). There's no guest OS or guest kernel to boot.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ App A │ │ App B │ │ App C │
│ (+ libs) │ │ (+ libs) │ │ (+ libs) │ <- no guest kernel
├─────────────┴──┴─────────────┴──┴─────────────┤
│ Container Runtime (Docker) │
├─────────────────────────────────────────────────┤
│ Host Operating System (kernel) │
├─────────────────────────────────────────────────┤
│ Hardware │
└─────────────────────────────────────────────────┘
| Virtual Machine | Container | |
|---|---|---|
| What's virtualized | Hardware — each VM has its own kernel | The OS — containers share the host kernel |
| Startup time | Seconds to minutes (booting a full OS) | Milliseconds to a couple seconds |
| Size | Gigabytes (a full OS image) | Megabytes to a couple hundred MB typically |
| Isolation strength | Very strong — separate kernels | Weaker than a VM — shared kernel means a kernel-level exploit can affect all containers |
| Density per host | Fewer, heavier | Many more, lighter |
| Typical use | Running fully different OSes side by side, strong security boundaries | Packaging and deploying application workloads consistently and densely |
Neither replaces the other outright — cloud providers commonly run containers inside VMs, getting a VM's strong isolation boundary around a host, and a container's fast, dense packaging on top of it.
Installing Docker
# Ubuntu/Debian — using Docker's official convenience script
$ curl -fsSL https://get.docker.com | sh
# Fedora/RHEL
$ sudo dnf install docker-ce docker-ce-cli containerd.io
# macOS / Windows
# Install Docker Desktop, which bundles a lightweight Linux VM
# since containers require a Linux kernel underneath.
$ docker --version
Docker version 27.1.1, build 6312585
On Linux, add your user to the docker group so you don't need sudo for every command:
$ sudo usermod -aG docker $USER
# log out and back in for this to take effect
Your first container
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
Digest: sha256:...
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
docker run did three things in sequence:
- Checked whether the
hello-worldimage existed locally — it didn't, so Docker pulled it from Docker Hub (the default public image registry). - Created a new container — a running instance of that image.
- Ran the image's default command inside that container, which just prints the message above and exits.
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
3f2a1b9c8e4d hello-world "/hello" 5 seconds ago Exited (0) 4 seconds ago nice_curie
docker ps lists running containers; -a includes stopped ones too, like this one, which already exited after printing its message.
Common mistakes
- Assuming a container is "as isolated as a VM" — containers share the host kernel, so a kernel vulnerability can potentially be exploited across container boundaries in a way it can't across VM boundaries.
- Forgetting that
docker runreuses a local image if one already exists with that tag, and doesn't automatically re-pull unless the image is removed or you specify a different tag — a stale local image can cause "but I updated the Dockerfile" confusion. - Running Docker commands with
sudoforever instead of adding your user to thedockergroup — harmless but a minor daily annoyance, and worth fixing once.
Interview questions
Q: What is the fundamental difference between a container and a virtual machine? A virtual machine virtualizes hardware — each VM runs its own full guest operating system and kernel on top of a hypervisor. A container virtualizes at the operating system level, sharing the host machine's kernel across all containers and using kernel features (namespaces, cgroups on Linux) for isolation, which makes containers far lighter and faster to start than VMs.
Q: Why are containers faster to start than virtual machines? A VM has to boot an entire guest operating system, including its own kernel, before your application can run. A container skips that entirely — it shares the already-running host kernel and just starts the application process directly inside an isolated namespace, so startup is closer to starting a regular process than booting a machine.
Q: Is a container as secure/isolated as a virtual machine? No, generally not. VMs have a much stronger isolation boundary because each one runs its own separate kernel; containers on the same host all share one kernel, so a serious kernel-level vulnerability could theoretically be leveraged to affect other containers on that host. This is why genuinely untrusted or highly sensitive workloads are often still run in separate VMs, or in containers running inside separate VMs, rather than containers alone.