Docker – What It Is and Why It Exists

June 15, 20265 min readUpdated 8/21/2026

You finish a feature, it works, you push it, and someone else pulls it and it does not run. A different Java. A different Node. A library the other machine has and yours does not. Docker exists to make that conversation unnecessary.

What a container actually is

A container is one process, running on your kernel, with a lie told to it about what the filesystem looks like.

That is nearly the whole idea. The process sees a root directory you gave it, its own network interface, its own process table — but it is scheduled by the same kernel as everything else on the machine. There is no second operating system underneath it.

Which is why people reach for the comparison with a virtual machine, and why the comparison is worth being precise about:

virtual machine                    container
─────────────────────              ─────────────────────
your app                           your app
libraries                          libraries
GUEST KERNEL + full OS      ←→     (nothing — shares the host kernel)
hypervisor                         container runtime
host kernel                        host kernel
hardware                           hardware

A VM boots an operating system, so it starts in tens of seconds and costs a gigabyte or two before your app has done anything. A container starts a process, so it starts in milliseconds and costs whatever your app costs.

The trade is isolation. A VM is separated from its neighbours by a hypervisor; a container is separated by kernel features — namespaces and cgroups — and a kernel bug is a shared problem. For running your own services that is a fine trade. For running untrusted code from strangers, people put a VM boundary back in.

Image and container are not the same word

This distinction causes more early confusion than anything else in Docker, so it is worth nailing down now.

An image is a filesystem plus some metadata, sitting on disk, not running. It is inert. It is closer to a class than to a zip file, in that you can make many things from it.

A container is one running (or stopped) instance of an image, with a thin writable layer of its own on top. Ten containers from one image share the image's bytes on disk and each get their own scratch space.

docker image ls        # images: the templates
docker ps              # containers: the running things
docker ps -a           # containers: including the ones that have stopped

The practical consequence: anything you change inside a running container is gone when that container is removed. Edit a config file in there, and you have edited the writable layer, not the image. Lesson 11 is about where to put things you actually want to keep.

What it is genuinely good at

Three things, in rough order of how much people care:

The environment travels with the code. The image contains the JRE, the libraries, the config file layout — all of it. "Works on my machine" becomes "works, and here is the machine".

One machine runs many services without them fighting. The demo application this track uses needs MySQL 8.4. Your laptop may already have MySQL 5.7 for something else. In containers that is not a conflict, it is two containers.

The thing you tested is the thing you deploy. Not a rebuild of it on the CI runner from the same source — the identical image, identified by a digest.

What it does not do

Worth saying early, because a lot of writing about Docker implies otherwise.

It does not make your application faster. It does not make it secure — a container running as root is running as root on your kernel, which is lesson 9. It does not restart your app when the machine reboots unless you ask it to, does not load-balance, does not roll out a new version without dropping requests, and does not move work off a failed host. Those are orchestrator jobs, and lesson 21 is honest about where the line falls.

And it is not free. An image is a few hundred megabytes you now have to build, store, pull and keep patched.

The versions this track is written against

Everything here was run on this machine, not copied from documentation. Where a number appears — an image size, a build time — it was measured.

$ docker version --format '{{.Server.Version}}'
27.4.0
$ docker compose version --short
2.31.0
$ docker buildx version
github.com/docker/buildx v0.19.2

$ docker info --format '{{.OperatingSystem}} | {{.Architecture}} | {{.Driver}}'
Docker Desktop | aarch64 | overlay2

That aarch64 matters more than it looks. It is an Apple Silicon Mac, so every image built here is arm64 by default, and most servers are not. That is the whole of lesson 19, and it is written from a machine that actually has the problem rather than one describing it.

The application every example comes from

Rather than a hello-world that teaches nothing about a real deployment, the examples are from a working pizza-ordering application:

pizza/
├── pizza-springboot-backend/   Java 21, Spring Boot 4.1, MySQL, Liquibase
├── pizza-react-frontend/       React 19 + Vite
├── pizza-angular-frontend/     Angular 21
└── compose.yaml                all of it, one command

So the multi-stage build is a real Maven build producing a real jar; the layer-caching lesson is about a real pom.xml; and the compose file starts a database, an API and a web server that actually serve a menu. Every file quoted in this track was built and run before it was quoted.

The lessons, in order

Foundations

  1. What It Is and Why It Exists
  2. Install It and Run Your First Container
  3. Images, Layers and Tags

Building images

  1. Writing a Dockerfile
  2. The Build Context and .dockerignore
  3. Layer Caching and Build Speed
  4. Multi-Stage Builds
  5. Choosing a Base Image and Keeping It Small
  6. Don't Run as Root

Running containers

  1. Ports, Networks and Container DNS
  2. Volumes, Bind Mounts and Keeping Your Data
  3. Environment Variables, Build Args and Secrets
  4. Logs, Exec and Debugging a Container

Compose

  1. Running a Multi-Container Stack
  2. depends_on, Healthchecks and Startup Order
  3. Profiles, Overrides and Multiple Files
  4. The Whole Application in One File

Shipping

  1. Registries, Tagging and Pushing an Image
  2. Multi-Platform Builds and exec format error
  3. Building and Pushing from GitHub Actions
  4. Running Containers in Production
  5. Interview Questions

Next: install Docker and run something.