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 startordocker 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