Code Safari

Chapter 115·Beginner·8 min read

Images vs Containers: The Two Core Ideas

A plain-English guide to the two central ideas in Docker — the image (a read-only blueprint of an app and its environment) and the container (a running instance of it), how layers make images efficient, where images come from, and the everyday docker commands to run, list, and stop them.

July 18, 2026

Docker has exactly two ideas at its centre, and almost everything else follows from keeping them straight: the image and the container. Beginners blur them constantly, and that blur is the source of most early confusion. Get the distinction clean — it's genuinely simple — and the commands, the workflow, and the whole mental model fall into place. This chapter is that distinction, plus the everyday commands that act on each.

Blueprint and building

Here's the whole idea in one analogy. An image is like an architect's blueprint: a complete, frozen specification of a building, sitting in a drawer. A container is the actual building constructed from that blueprint — a real, standing structure you can walk through. And critically: from one blueprint you can build many identical buildings.

Mapping that back to Docker:

  • An image is a read-only, packaged template — your app plus its entire environment, frozen into one file-like artefact. It doesn't run; it's the recipe.
  • A container is a running instance of an image — the app actually executing, brought to life from that template.

The "one image, many containers" part is not a technicality — it's the point. The same image can run as three identical containers behind a load balancer, or as one container on your laptop and another on a server, all guaranteed identical because they descend from the same frozen template. This is exactly the "runs the same everywhere" promise from the last chapter, made mechanical.

The image: a read-only package

Let's look harder at the image. It bundles several things into one immutable unit:

  • A minimal filesystem — a stripped-down set of operating-system files (say, a lean Ubuntu or Alpine Linux base) so your app has the basics it expects.
  • Your application code.
  • Its dependencies — the exact libraries and runtime versions it needs.
  • A little metadata — which command to run when it starts, which port it uses, and so on.

The defining property is that an image is read-only and immutable. Once built, it never changes. If you need a new version of your app, you don't edit an image — you build a fresh one. This immutability is what makes images trustworthy: an image that worked in testing is byte-for-byte the same image that runs in production. Nothing drifted in between.

You'll refer to images by name and tag, written name:tag — like python:3.11 or nginx:latest. The tag (3.11, latest) is the version label. python:3.11 and python:3.9 are two different images of the same software.

The container: the image brought to life

You turn an image into a running container with one command — docker run:

$ docker run nginx

That takes the nginx image and starts it as a live container: a real process, serving web pages, running directly on the host kernel (as the last chapter explained). The magic is what happens on top of the read-only image. Docker adds a thin writable layer just for that container — a scratch space where the running app can create and change files. The underlying image stays pristine and untouched; only the container's own writable layer records changes.

Read-only image (the blueprint)
docker run
Add a thin writable layer
Running container (the instance)
From frozen image to living container

This is why containers are disposable. Delete a container and you throw away only its little writable layer — the image is completely unaffected, ready to spawn a fresh, clean container instantly. That disposability is a feature you'll lean on constantly: something got into a weird state? Kill the container, start a new one from the same image, and you're back to a known-good starting point in milliseconds. (It also raises an obvious question — what about data you want to keep? — which is exactly what chapter four, on volumes, answers.)

Layers: why images are efficient

Images have one more piece of cleverness worth understanding, because it explains why Docker is fast and doesn't devour your disk. An image isn't one solid blob — it's built as a stack of layers, each layer a slice of the filesystem representing one build step.

Imagine building an image: start from a base OS layer, add a layer that installs Python, add a layer with your app's libraries, add a layer with your code. Each step is its own layer, stacked on the ones below.

   ┌─────────────────────────┐
   │  Your app code          │  ← layer 4
   ├─────────────────────────┤
   │  App dependencies       │  ← layer 3
   ├─────────────────────────┤
   │  Python 3.11 installed   │  ← layer 2
   ├─────────────────────────┤
   │  Minimal OS (base image) │  ← layer 1
   └─────────────────────────┘
      a running container adds
      one thin WRITABLE layer on top
An image is a stack of read-only layers

Two big wins come from this. First, layers are cached: rebuild your image after changing only your code, and Docker reuses the unchanged base, Python, and dependency layers from before — only the final code layer is rebuilt. Builds that would take minutes take seconds. Second, layers are shared between images: if ten different images all build on python:3.11, that base layer is stored once on disk and shared, not copied ten times. This layering is why an ecosystem of thousands of images doesn't require a warehouse of disk space — and understanding it now pays off directly when we write a Dockerfile next chapter, because the order of your build steps controls the caching.

Where images come from: registries

You don't build every image yourself. A huge amount of the value of Docker is that battle-tested software is already packaged and waiting for you in a registry — a shared library of images you can download. The default public one is Docker Hub.

docker pull downloads an image from a registry to your machine:

$ docker pull postgres:16      # grab the official PostgreSQL 16 image

And you rarely even need that as a separate step: docker run postgres:16 will automatically pull the image if you don't already have it, then start it. In practice this means you can run a full PostgreSQL database, an nginx web server, or a Redis cache in seconds, without installing a single thing on your actual system — the software lives entirely inside the container. When you're done, remove the container and your machine is exactly as clean as before. For anyone who's spent an afternoon wrestling a database install onto their laptop, this alone sells Docker.

The everyday commands

You now know enough to read the handful of commands you'll use constantly. Notice how they split cleanly into image commands and container commands — the distinction from the top of this chapter, made practical:

# Images (the blueprints)
$ docker images                 # list images you have locally
$ docker pull node:20           # download an image from a registry
$ docker rmi node:20            # remove an image
 
# Containers (the running instances)
$ docker run nginx              # start a new container from an image
$ docker run -d -p 8080:80 nginx  # -d run in background, -p map a port
$ docker ps                     # list running containers
$ docker ps -a                  # list all containers, including stopped
$ docker stop <id>              # stop a running container
$ docker rm <id>                # remove a stopped container

A couple of those flags echo the last guide directly: -d runs the container detached (in the background, just like & on a normal process), and -p 8080:80 maps a port — connecting port 8080 on your host to port 80 inside the container, so a browser on your machine can reach the web server sealed inside it. (Ports between host and container are their own small topic — that's chapter five, tomorrow's territory.) And docker ps, listing running containers, is the deliberate cousin of the ps command you used to list processes — fitting, since a container is an isolated process.

What this unlocks

Step back and see how much the two-idea distinction organises. Building software produces an image; sharing it means pushing and pulling images through a registry; running it means starting containers. Images are immutable and permanent; containers are disposable and cheap. One image spawns many identical containers. Nearly every Docker command you'll ever type is an operation on one or the other, and knowing which is which is most of the battle.

So far, though, we've only run images other people built. The real power comes when you package your own application into an image — and that's done with a small recipe file called a Dockerfile. Writing one, and doing it well, is the next chapter.

Images vs Containers: The Two Core Ideas | Code Safari