In this lesson: Explain what a container is and why it removes environment differences.
"It works on my machine" is not a joke about careless developers. It is an accurate description of a real problem: your laptop has PHP 8.2, the server has 8.1; you have ImageMagick installed, the server does not; your database is MySQL 8, production is MariaDB. The code is identical and the behaviour is not.
A container packages an application together with everything it needs to run — the runtime, the libraries, the system tools — into one artefact that behaves identically wherever it is started.
How it is different from a virtual machine
A virtual machine emulates a whole computer. It boots its own operating system kernel, which takes gigabytes of disk, hundreds of megabytes of memory and the better part of a minute.
A container does not. It shares the host's kernel and isolates only what the application sees: its own filesystem, its own process list, its own network. What remains is your application and its dependencies — tens of megabytes, started in under a second.
| Virtual machine | Container | |
|---|---|---|
| Contains | A full OS plus your app | Your app plus its dependencies |
| Size | Gigabytes | Tens to hundreds of megabytes |
| Start time | Tens of seconds | Under a second |
| How many per server | A handful | Dozens or hundreds |
| Isolation | Very strong — separate kernel | Strong, but the kernel is shared |
The three words you need
| Term | What it is |
|---|---|
| Image | A read-only template: a filesystem plus instructions for what to run. Built once, never changed. |
| Container | A running instance of an image. You can start twenty from one image. |
| Registry | A place images are stored and shared, like a Git remote but for images. |
The relationship is the same as a class and its objects, or a recipe and the meals you cook from it. The image is the fixed thing; containers are disposable.
Immutability is the point
You do not log into a container and fix it. If something needs changing, you change the source, build a new image, and replace the container. This feels wasteful and is the single most valuable habit containers teach.
The alternative — servers you patch, tune and repair over years — produces machines nobody can rebuild. The configuration lives in the muscle memory of whoever set it up, and when that machine dies you discover you cannot recreate it. Containers make the build reproducible by making the running thing disposable.
The commands you will use constantly
docker ps # running containers
docker ps -a # including stopped ones
docker images # images on this machine
docker logs -f my-app # follow a container's output
docker exec -it my-app sh # get a shell inside a running container
docker stop my-app
docker rm my-app
docker system df # how much disk is Docker using?
docker system prune # reclaim space from stopped containers and unused images
docker system prune -af --volumes on a schedule, and read the flags before you run it on anything you care about.
Try it yourself
Run docker run --rm -it alpine sh. You are now inside a container. Run ls / — a complete Linux filesystem. Run ps aux — almost no processes, because you are seeing only this container's. Create a file, exit, and start the same command again: the file is gone, because you got a fresh container from an unchanged image. That is immutability in one minute.