How to use this page
Find the job you're trying to do, and copy the command. Words in capitals, like NAME, IMAGE or FILE, are yours to replace. IMAGE is a name and a tag, like nginx:1.28. Every row links to the lesson that explains it. Use the print button at the top to keep a copy next to you.
Two habits that make every command here easier:
- When a container misbehaves, run
docker ps -a, thendocker logs NAME. The first shows its exit code, the second shows what it said before it stopped. Both work after it has stopped. - Pin your tags.
nginx:1.28means the same thing next month.nginx:latestmight not.
When a container won't work
Start here when something's wrong. Find what you're seeing, and it tells you where to look.
| You see | It usually means | Do this | Lesson |
|---|---|---|---|
Exited (1) or any number but 0 in docker ps -a | The program inside failed and stopped. 0 means it finished fine | docker logs NAME | 2 |
Exit code 137 | It used more memory than its limit, and was killed (OOM: out of memory) | docker inspect NAME --format '{{.State.OOMKilled}}'. If true, give it more memory or make it use less | 5 |
Conflict. The container name "/NAME" is already in use | Names must be unique, even among stopped containers | docker rm -f NAME, or pick another name | 2 |
| Port is already allocated | Something else is using that laptop port | Change the first number: -p 8081:80 | 2 |
manifest unknown or manifest for IMAGE not found | There's no image with that tag | Check the name and tag on the registry | 3 |
exec format error | The image was built for a different processor, usually arm64 (an M-series Mac) on an amd64 server | Rebuild with --platform linux/amd64 | 3, 8 |
Connection refused from one container to another | The address exists, but nothing is listening. Inside a container, localhost is the container itself | Use the other container's name, like cache, and check the port | 6 |
Name or service not known | That name can't be found on the network: misspelled, or not on the same network | Check the spelling and --network | 6 |
(unhealthy) in docker ps | Its HEALTHCHECK keeps failing | docker inspect NAME, then look under Health | 7 |
| A setting seems to be ignored | Often a misspelled variable name. The app quietly uses its default | docker exec NAME env and check the spelling | 5 |
Running containers
| To | Run | Lesson |
|---|---|---|
| Check Docker is working | docker version | 1 |
| Run a test container | docker run hello-world | 2 |
| Run in the background, with a name and a port | docker run -d -p 8080:80 --name NAME IMAGE | 2 |
| Run once, with a shell, and remove it after | docker run --rm -it IMAGE sh | 2 |
| List running containers | docker ps | 2 |
| List all containers, including stopped ones | docker ps -a | 2 |
| Stop one | docker stop NAME | 2 |
| Start a stopped one again | docker start NAME | 2 |
| Remove one, even if it's running | docker rm -f NAME | 2 |
In -p 8080:80, the first number is your laptop's port and the second is the container's. Only the first has to be free.
Inside a running container
| To | Run | Lesson |
|---|---|---|
| Its logs | docker logs NAME | 2 |
| Keep watching its logs (Ctrl+C to stop) | docker logs -f NAME | 2 |
| Run one command inside it | docker exec NAME cat /etc/os-release | 2 |
Open a shell inside it (exit to leave) | docker exec -it NAME sh | 2 |
| See its settings | docker exec NAME env | 5 |
| Memory and CPU each container is using | docker stats --no-stream | 5 |
| Everything Docker knows about it | docker inspect NAME | 5 |
| Which user it runs as | docker run --rm IMAGE whoami | 7 |
Changes made inside a container disappear when it's removed. Anything worth keeping goes in a volume, or in the image.
Images and registries
An image name reads registry/owner/name:tag. nginx:1.28 is short for docker.io/library/nginx:1.28: Docker Hub's official image.
| To | Run | Lesson |
|---|---|---|
| Download an image | docker pull IMAGE | 3 |
| List the images you have | docker image ls | 3 |
| See its layers, and what made each one | docker image history IMAGE | 3 |
| Which processor it's for | docker image inspect IMAGE --format '{{.Os}}/{{.Architecture}}' | 3 |
| Which processors a registry image supports | docker buildx imagetools inspect IMAGE | 3 |
| Its digest: an ID that never changes, unlike a tag | docker image inspect IMAGE --format '{{index .RepoDigests 0}}' | 3 |
| Delete an image | docker rmi IMAGE | 3 |
Building your own image
| To | Run | Lesson |
|---|---|---|
| Build from the Dockerfile in this folder | docker build -t NAME:TAG . | 4 |
| Build from a different Dockerfile | docker build -f FILE -t NAME:TAG . | 7 |
| Build for an amd64 server, from any laptop | docker build --platform linux/amd64 -t NAME:TAG . | 8 |
| See your image's size | docker image ls NAME | 4 |
| Dockerfile line | What it does | Lesson |
|---|---|---|
FROM python:3.13-slim | The image to start from | 4 |
WORKDIR /app | The folder later lines work in | 4 |
COPY requirements.txt . | Copies files from your folder into the image | 4 |
RUN pip install ... | Runs a command while building, and saves the result as a layer | 4 |
ENV KEY="value" | Sets an environment variable for everything after it | 7 |
EXPOSE 8000 | A note that the app listens on 8000. It doesn't publish anything; -p does that | 4 |
USER app | Runs everything after it, and the container, as that user instead of root | 7 |
HEALTHCHECK ... CMD ... | A check Docker runs on a schedule to mark the container healthy or unhealthy | 7 |
CMD ["flask", "run"] | The command that runs when the container starts | 4 |
FROM ... AS builder, then COPY --from=builder | A multi-stage build: build in one stage, copy only the result into a clean one | 7 |
Order matters. Docker reuses every layer up to the first line that changed. Copy the list of libraries and install them first, then copy your code, so changing your code doesn't reinstall everything.
Settings, data and limits
| To | Run | Lesson |
|---|---|---|
| Pass one setting | docker run -e GREETING=Bonjour IMAGE | 5 |
| Pass settings from a file | docker run --env-file settings.env IMAGE | 5 |
| Pass a setting from your own shell, without typing its value | docker run -e DATABASE_URL IMAGE | 5 |
| Make a named volume | docker volume create NAME | 5 |
| Attach a volume | docker run -v NAME:/data IMAGE | 5 |
| Share this folder with the container (a bind mount) | docker run -v "$PWD":/app IMAGE | 5 |
| List volumes | docker volume ls | 5 |
| Delete a volume, and its data | docker volume rm NAME | 5 |
| Limit memory and CPU | docker run --memory 128m --cpus 0.5 IMAGE | 5 |
| The exit code of the last command you ran | echo $? | 5 |
| Data written to | Lasts until | Good for |
|---|---|---|
| The container itself | The container is removed | Nothing you want to keep |
| A named volume | You delete the volume | Databases, caches, anything the app saves |
| A bind mount | You delete the files: it's your own folder | Development: edit code on your laptop, run it in the container |
Networks and Compose
Containers on the same network find each other by name. Compose puts every service in its file on one network for you.
| To | Run | Lesson |
|---|---|---|
| Make a network | docker network create NAME | 6 |
| Run a container on it | docker run -d --name cache --network NAME IMAGE | 6 |
| Delete a network | docker network rm NAME | 6 |
Build and start everything in compose.yaml | docker compose up -d --build | 6 |
Apply changes to compose.yaml | docker compose up -d | 6 |
| See its containers | docker compose ps | 6 |
| One service's logs | docker compose logs web | 6 |
| Stop and remove it all, keeping volumes | docker compose down | 6 |
| Stop and remove it all, volumes too | docker compose down -v | 6 |
A small compose.yaml: a web app you build, and Redis from an existing image, with its data in a volume.
services:
web:
build: .
ports:
- "8000:8000"
environment:
REDIS_HOST: cache
depends_on:
- cache
cache:
image: redis:7.2.7-alpine
volumes:
- cache-data:/data
volumes:
cache-data:
Smaller, safer images
| Do | Why | Lesson |
|---|---|---|
Start from a -slim or -alpine base | Less software to download, and less for a flaw to hide in | 7 |
Add a .dockerignore | Keeps .git, .env and other secrets out of the build | 7 |
| Use a multi-stage build | Compilers and build tools stay behind in the first stage | 7 |
| Run as a normal user, with a fixed number | useradd --uid 1000 and USER app. Kubernetes can only check a number is not root | 7, 8 |
| Copy only what the app needs | COPY . . copies everything, secrets included, unless .dockerignore stops it | 7 |
| Scan the image before you ship it | Trivy or Docker Scout list known flaws in what's inside | 7 |
| Health checks look at the app only | A check that depends on a database marks a good app unhealthy when the database is down | 7 |
A starting .dockerignore:
.git
.env
*.env
__pycache__
.venv
compose.yaml
Shipping to a registry
| To | Run | Lesson |
|---|---|---|
| Sign in to Docker Hub | docker login | 8 |
| Give your image a registry name | docker tag NAME:TAG "$DOCKER_ID/NAME:TAG" | 8 |
| Push it | docker push "$DOCKER_ID/NAME:TAG" | 8 |
| Set up a builder that can build for several processors (once) | docker buildx create --name multi --use | 8 |
| Build for amd64 and arm64, and push | docker buildx build --platform linux/amd64,linux/arm64 -t "$DOCKER_ID/NAME:TAG" --push . | 8 |
| Load a local image into a kind cluster, no registry needed | kind load docker-image NAME:TAG --name learn | 8 |
Cleaning up
| To | Run | Lesson |
|---|---|---|
| See what's using disk space | docker system df | 3 |
| Remove stopped containers, unused networks and dangling images (it asks first) | docker system prune | 3 |
| Remove several containers at once | docker rm -f NAME1 NAME2 | 2 |
| Remove several images at once | docker rmi IMAGE1 IMAGE2 | 4 |
Every Docker idea, in Kubernetes
When you move to Kubernetes, the Kubernetes cheatsheet picks up from here.
| In Docker | In Kubernetes |
|---|---|
docker run one container | A Pod, usually kept running by a Deployment |
compose.yaml | Kubernetes YAML files, applied with kubectl apply |
-p 8000:8000 | A Service, plus kubectl port-forward for testing |
| Service names on a Compose network | Service names, through the cluster's DNS |
-e, --env-file | env, ConfigMaps and Secrets |
| A named volume | A PersistentVolumeClaim |
--memory, --cpus | Requests and limits |
HEALTHCHECK | Readiness and liveness probes |
USER | securityContext, such as runAsNonRoot |