Docker has two core concepts you must understand before anything else: images and containers.

Beginners often confuse them. Once you understand the difference, everything else in Docker makes sense.

What is a Docker Image?

A Docker image is a read-only template. It contains everything your application needs to run: the OS filesystem, libraries, dependencies, and your application code.

An image is built from a Dockerfile. Think of an image like a class in object-oriented programming. It defines what a container will look like.

You do not run an image directly. You create a container from it.

What is a Docker Container?

A container is a running instance of an image. When you run a container, Docker creates a writable layer on top of the image and starts the process.

Using the class analogy again:

  • Image = class definition
  • Container = object created from that class
  • You can create many containers from the same image
Image: nginx:1.27
├── Container 1: nginx (port 8080)
├── Container 2: nginx (port 8081)
└── Container 3: nginx (port 8082)

Three containers, all from the same image. Each container is isolated and independent.

How Image Layers Work

A Docker image is made of layers. Each instruction in a Dockerfile creates a new layer.

FROM ubuntu:24.04        # Layer 1: base OS
RUN apt update           # Layer 2: package index
RUN apt install -y curl  # Layer 3: curl installed
COPY app.py /app/        # Layer 4: your code

These layers are stacked on top of each other to form the final image.

(Note: In real-world Dockerfiles, you should combine commands like apt update and apt install into a single layer to avoid caching issues, but we show them separately here to illustrate how layers work.)

The key benefit: layers are shared and cached. If two images both use ubuntu:24.04, Docker stores that layer only once on disk. This saves a lot of space.

When you run a container, Docker adds one more layer on top — a writable layer. All changes you make inside the container (creating files, writing to disk) go into this writable layer. The original image layers stay read-only.

Container (writable layer)
├── Layer 4: your code     (read-only)
├── Layer 3: curl          (read-only)
├── Layer 2: package index (read-only)
└── Layer 1: ubuntu base   (read-only)

When the container is removed, the writable layer is gone. This is why containers are ephemeral by default.

Pulling Images from Docker Hub

Docker Hub is the default public registry. It has thousands of official images for popular software: nginx, postgres, redis, python, node, and more.

Pull an image with:

docker pull nginx

This pulls the nginx:latest image. To pull a specific version:

docker pull nginx:1.27
docker pull python:3.13-slim
docker pull postgres:17

View all images on your system:

docker images

Output:

REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
nginx        1.27      a1b2c3d4e5f6   2 days ago     192MB
python       3.13-slim  1a2b3c4d5e6f  5 days ago     130MB

Running Your First Container

Run a container with docker run:

docker run nginx

This starts an nginx container in the foreground. Press Ctrl+C to stop it.

Run in detached mode

To run in the background, add the -d flag:

docker run -d nginx

Docker prints the container ID and returns to the prompt. The container runs in the background.

Map a port

Nginx listens on port 80 inside the container. To access it from your browser, map port 80 inside the container to port 8080 on your host:

docker run -d -p 8080:80 nginx

Now open http://localhost:8080 in your browser. You see the nginx welcome page.

The -p format is: hostPort:containerPort.

Name your container

Give your container a name instead of the auto-generated one:

docker run -d -p 8080:80 --name mywebserver nginx

Run interactively

To run a container and get a shell inside it:

docker run -it ubuntu:24.04 bash
  • -i — interactive (keep stdin open)
  • -t — allocate a terminal

You are now inside the container. Type exit to leave.

Remove the container when it stops

Add --rm to automatically remove the container when it stops:

docker run --rm -it ubuntu:24.04 bash

Useful for one-off containers you don’t want to keep.

Container Lifecycle

A container goes through these states:

Created → Running → Stopped → Removed
  • Created: docker create — creates the container but does not start it
  • Running: docker start or docker run — the container is running
  • Stopped: docker stop — gracefully stops the container (sends SIGTERM, waits, then SIGKILL)
  • Removed: docker rm — deletes the container permanently

Stopped containers still exist on disk until you remove them. This is a common source of confusion.

Managing Containers

List running containers

docker ps

List all containers (including stopped)

docker ps -a

Stop a container

docker stop mywebserver

Start a stopped container

docker start mywebserver

Remove a container

docker rm mywebserver

You cannot remove a running container. Stop it first, or force it:

docker rm -f mywebserver

View container logs

docker logs mywebserver

Follow logs in real time:

docker logs -f mywebserver

Execute a command in a running container

docker exec -it mywebserver bash

This opens a bash shell inside the running container. Very useful for debugging.

Inspect a container

docker inspect mywebserver

This returns a large JSON object with all the container’s configuration: network settings, volumes, environment variables, and more.

Cleaning Up

Over time, stopped containers and unused images take up disk space.

Remove all stopped containers:

docker container prune

Remove unused images:

docker image prune

Remove everything unused (containers, images, networks, cache):

docker system prune

Check disk usage:

docker system df

Common Mistakes

Not using --rm for throwaway containers

When you run docker run -it ubuntu bash to test something, the container stays on disk after you exit. Over time, you accumulate dozens of stopped containers. Use --rm for any container you don’t need to keep.

Confusing image ID with container ID

docker images shows image IDs. docker ps shows container IDs. They are different. An image ID stays the same. Each container gets its own unique ID when you run it.

Forgetting that stopped containers still exist

Running docker stop myapp does not delete the container. It stops it. The container is still on disk, with its writable layer. To clean up, run docker rm myapp after stopping it — or use --rm when you start it.

What’s Next?

Now you know how images and containers work. In the next tutorial, you will learn how to write your own Dockerfile and build a custom image.

Next: Docker Tutorial #4: Dockerfile — Build Your Own Docker Image