---
title: "01 — What Is Docker? Containers, VMs, and the End of 'Works on My Machine'"
uid: what-is-docker
tags: ["virtualization", "oci", "roadmap:docker", "docker", "containers", "fundamentals"]
excerpt: "A container is one process on your machine, walled off by Linux kernel primitives, with its filesystem and dependencies frozen into a portable image. Not a faster VM."
date: 2026-08-13T03:28:15+0000
source: https://www.aveshina.my.id/en/blog/what-is-docker
---

"A faster virtual machine" was my Docker model, and it leaked in every direction — resource claims, startup, isolation. The idea that finally separated the threads: **a container is just a process on your machine, walled off by Linux kernel primitives, with its filesystem and dependencies frozen into a portable image.** [1][3]

The framing that landed for me is the contrast, not the tool in isolation. Before containers, shipping an app meant shipping it *with* an operating system, or praying the destination machine had the right OS, library versions, and config. Docker collapses both problems by packaging the app and everything it needs into one unit that runs the same everywhere, while reusing the host's kernel instead of booting a fresh one [1][5]. Once I could see how that differs from a VM, most of Docker's design stopped feeling arbitrary.

## The problem: "works on my machine"

The thing I had to see clearly is *why* anyone built this. The "need for containers" is a story about environment drift — the set of versions and config my app needs on my laptop slowly diverging from what a teammate has, and from what production runs [4]. The classic symptom is the phrase every team learns to dread: "it works on my machine."

Containers solve that by standardizing the runtime environment. The app, its language runtime, its libraries, its system tools, and its config all go into one image. That image is a fixed, immutable artifact. Run it on my laptop, on a teammate's laptop, or on a production server, and it boots the same way, because the only thing it borrows from the host is the kernel [1][5]. The drift simply has nowhere to hide.

## Bare metal, VM, container

The cleanest way I found to pin down a container is to put it next to the two things it's not. Three deployment models, one trade-off axis — isolation versus overhead:

```figure
<svg viewBox="0 0 720 320" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Three deployment models side by side. Bare metal: one app running directly on the host operating system and hardware, maximum performance, no isolation. Virtual machine: two apps each inside their own guest OS, stacked on a hypervisor on the host OS and hardware — strong isolation but heavy. Container: three apps each in a lightweight container box sharing one host OS kernel on the hardware — isolation with low overhead.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- Column headers -->
    <g text-anchor="middle" font-size="12" font-weight="700">
      <text x="120" y="22" fill="#7f1d1d">Bare metal</text>
      <text x="360" y="22" fill="#422006">Virtual machine</text>
      <text x="600" y="22" fill="#1e1b4b">Container</text>
    </g>
    <g text-anchor="middle" font-size="10" fill="#64748b">
      <text x="120" y="37">one app on the metal</text>
      <text x="360" y="37">apps each in a guest OS</text>
      <text x="600" y="37">apps on a shared kernel</text>
    </g>

    <!-- BARE METAL -->
    <rect x="40" y="52" width="160" height="44" rx="6" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="120" y="78" font-size="11" font-weight="700" fill="#7f1d1d" text-anchor="middle">App</text>
    <rect x="40" y="104" width="160" height="32" rx="6" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="120" y="125" font-size="10" font-weight="700" fill="#422006" text-anchor="middle">Host OS kernel</text>
    <rect x="40" y="146" width="160" height="32" rx="6" fill="#e2e8f0" stroke="#64748b" stroke-width="1.5"/>
    <text x="120" y="167" font-size="10" font-weight="700" fill="#334155" text-anchor="middle">Hardware</text>

    <!-- VM -->
    <g>
      <rect x="290" y="52" width="62" height="36" rx="5" fill="#fde68a" stroke="#ca8a04" stroke-width="1.3"/>
      <text x="321" y="74" font-size="9" font-weight="700" fill="#422006" text-anchor="middle">App A</text>
      <rect x="368" y="52" width="62" height="36" rx="5" fill="#fde68a" stroke="#ca8a04" stroke-width="1.3"/>
      <text x="399" y="74" font-size="9" font-weight="700" fill="#422006" text-anchor="middle">App B</text>
    </g>
    <g>
      <rect x="290" y="96" width="62" height="22" rx="4" fill="#fef3c7" stroke="#ca8a04" stroke-width="1.1"/>
      <text x="321" y="111" font-size="8.5" fill="#422006" text-anchor="middle">Guest OS</text>
      <rect x="368" y="96" width="62" height="22" rx="4" fill="#fef3c7" stroke="#ca8a04" stroke-width="1.1"/>
      <text x="399" y="111" font-size="8.5" fill="#422006" text-anchor="middle">Guest OS</text>
    </g>
    <rect x="280" y="128" width="160" height="26" rx="6" fill="#fee2e2" stroke="#dc2626" stroke-width="1.5"/>
    <text x="360" y="145" font-size="10" font-weight="700" fill="#7f1d1d" text-anchor="middle">Hypervisor</text>
    <rect x="280" y="162" width="160" height="28" rx="6" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="360" y="180" font-size="10" font-weight="700" fill="#422006" text-anchor="middle">Host OS / Hardware</text>

    <!-- CONTAINER -->
    <g>
      <rect x="525" y="52" width="48" height="36" rx="5" fill="#c7d2fe" stroke="#6366f1" stroke-width="1.3"/>
      <text x="549" y="74" font-size="9" font-weight="700" fill="#1e1b4b" text-anchor="middle">App</text>
      <rect x="577" y="52" width="48" height="36" rx="5" fill="#c7d2fe" stroke="#6366f1" stroke-width="1.3"/>
      <text x="601" y="74" font-size="9" font-weight="700" fill="#1e1b4b" text-anchor="middle">App</text>
      <rect x="629" y="52" width="48" height="36" rx="5" fill="#c7d2fe" stroke="#6366f1" stroke-width="1.3"/>
      <text x="653" y="74" font-size="9" font-weight="700" fill="#1e1b4b" text-anchor="middle">App</text>
    </g>
    <rect x="525" y="96" width="152" height="22" rx="4" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.1"/>
    <text x="601" y="111" font-size="8.5" fill="#1e1b4b" text-anchor="middle">Bins / Libs (image)</text>
    <rect x="525" y="128" width="152" height="34" rx="6" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="601" y="149" font-size="10" font-weight="700" fill="#422006" text-anchor="middle">Shared host kernel</text>
    <rect x="525" y="170" width="152" height="26" rx="6" fill="#e2e8f0" stroke="#64748b" stroke-width="1.5"/>
    <text x="601" y="187" font-size="10" font-weight="700" fill="#334155" text-anchor="middle">Hardware</text>

    <!-- overhead callouts -->
    <g text-anchor="middle" font-size="10" font-style="italic">
      <text x="120" y="210" fill="#7f1d1d">no isolation · max perf</text>
      <text x="360" y="210" fill="#422006">strong isolation · heavy (full OS each)</text>
      <text x="600" y="210" fill="#1e1b4b">process isolation · lightweight</text>
    </g>
  </g>
</svg>
```

- **Bare metal** runs the app directly on the host operating system and hardware. Nothing is duplicated, so performance is maximal — but there's no isolation, and the app is welded to that one machine's config [6].
- **Virtual machines** sit on a *hypervisor* — a layer that fakes a complete computer — and each VM runs its own full *guest* operating system. Isolation is excellent, because each app genuinely believes it owns a separate machine. The cost is overhead: booting and running a whole OS per app burns RAM, disk, and CPU [7].
- **Containers** skip the guest OS. Each container holds the app plus its own bins and libraries, but they all share the *host* kernel. That makes them tiny — megabytes, not gigabytes — and near-instant to start, because no OS boots [1][3].

The part that clicked: a VM virtualizes *hardware*; a container virtualizes *the environment around one process*. Same isolation goal, very different cost.

## What a container actually is

With the contrast in place, the definition gets precise. A container is a lightweight, standalone, executable package — application code plus its runtime, libraries, and system tools, all wrapped so the whole thing runs in an isolated environment [3]. When you start it, the host kernel runs the app as an ordinary process, but kernel features (namespaces, cgroups — covered in the next post) make that process believe it has its own filesystem, network, process list, and user tree [1]. From the inside, it looks like a private machine. From the outside, it's one PID in ps.

Two artifacts make this work, and I conflated them for too long:

- An **image** is the frozen, read-only blueprint — the layered filesystem plus metadata. It's a file you can push and pull like any other.
- A **container** is a *running instance* of an image — the image plus a writable scratch layer on top, plus a live process. One image, many containers; stop the container and the writable layer goes away unless you persist it [10].

## OCI: the reason it's not just "Docker"

The last idea worth holding onto is that Docker is one implementation of an open standard. The **Open Container Initiative (OCI)** is a Linux Foundation project that defines the format for container images and the runtime that executes them [2][8]. Practically, that means an image built with Docker can be run by other runtimes (containerd, runc, Podman), and the ecosystem can't be locked into one vendor. When the roadmap talks about "Docker and OCI," this is the whole point — interoperability through agreed specifications [2].

This is why I no longer think of "Docker" and "container" as synonyms. Docker made containers famous and still has the smoothest tooling; the *format* and the *concept* belong to OCI now [8].

## How I use this

The practical payoff is a decision check before I reach for any virtualization. When I need strong security isolation between mutually distrustful tenants, I lean toward a VM — a full kernel boundary is harder to escape than a process boundary. When I need to ship an app that runs the same way across dev, CI, and prod, with fast starts and low overhead, I reach for a container. Most of my work is the second case, which is why Docker is the default in my stack. And when I describe a container to someone now, I lead with "a walled-off process sharing the host kernel," not "a lightweight VM" — because the process framing is what actually predicts how containers behave.

## References

[1] Docker, Inc., "What is a Container?," docker.com, 2024. [Online]. Available: [https://www.docker.com/resources/what-container/](https://www.docker.com/resources/what-container/)

[2] Open Container Initiative, "Open Container Initiative," opencontainers.org, 2024. [Online]. Available: [https://opencontainers.org/](https://opencontainers.org/)

[3] Amazon Web Services, "Introduction to Containers," AWS Skill Builder, 2024. [Online]. Available: [https://explore.skillbuilder.aws/learn/course/106/introduction-to-containers](https://explore.skillbuilder.aws/learn/course/106/introduction-to-containers)

[4] Red Hat, "What are containers?," redhat.com, 2024. [Online]. Available: [https://www.redhat.com/en/topics/containers](https://www.redhat.com/en/topics/containers)

[5] Docker, Inc., "Docker Overview," Docker Docs, 2024. [Online]. Available: [https://docs.docker.com/](https://docs.docker.com/)

[6] Cloud Native Computing Foundation, "Bare Metal Machine," CNCF Glossary, 2024. [Online]. Available: [https://glossary.cncf.io/bare-metal-machine/](https://glossary.cncf.io/bare-metal-machine/)

[7] Microsoft Azure, "What is a Virtual Machine?," Azure Cloud Computing Dictionary, 2024. [Online]. Available: [https://azure.microsoft.com/en-au/resources/cloud-computing-dictionary/what-is-a-virtual-machine](https://azure.microsoft.com/en-au/resources/cloud-computing-dictionary/what-is-a-virtual-machine)

[8] Wikipedia, "Open Container Initiative," 2024. [Online]. Available: [https://en.wikipedia.org/wiki/Open_Container_Initiative](https://en.wikipedia.org/wiki/Open_Container_Initiative)

```quiz
Q: What is the single biggest difference between a VM and a container?
- A VM runs each app on a shared kernel; a container runs a full guest OS per app
- A container shares the host kernel; a VM runs a full guest OS per app
correct: 1
explain: VMs virtualize hardware and each carries a complete guest OS, which is heavy. Containers virtualize the environment around one process and share the host kernel, so they are lightweight.

Q: The "works on my machine" problem is a symptom of…
- environment drift between dev, teammate, and production machines
- containers being too slow to start
- HTTP being stateless
correct: 0
explain: Without a standardized package, the set of library versions and config an app needs diverges across machines. Containers solve it by freezing the whole runtime into one portable image.

Q: Which statement about images vs. containers is correct?
- An image is the frozen read-only blueprint; a container is a running instance of an image
- A container is the blueprint; an image is a running instance
- They are two names for the same thing
correct: 0
explain: The image is the layered, immutable artifact you push and pull. A container is a live process — the image plus a writable scratch layer — that disappears when stopped unless its data is persisted.

Q: Why does the roadmap separate "Docker" from "OCI"?
- Docker is a brand of container registry; OCI is a competing runtime
- OCI defines the open standard for image format and runtime, so Docker images interoperate with other runtimes like containerd and runc
correct: 1
explain: The Open Container Initiative specifies the image format and runtime spec. Docker is one implementation of that spec, which is why its images can run elsewhere and the ecosystem avoids vendor lock-in.

Q: A container is best described to a teammate as…
- a tiny virtual machine that boots its own kernel
- a walled-off process sharing the host kernel, packaged with its dependencies
correct: 1
explain: From the host's perspective a container is just a process whose filesystem, network, and process view are isolated by kernel features. It does not boot a kernel of its own.
```
