I still remember the first time “it works on my machine” stopped being a punchline and actually became a real problem I had to solve. Docker fixed that for me almost overnight — but only after I stopped guessing at commands and actually learned the tool properly. Since then, I’ve built this cheat sheet the same way I built the Git one: as the reference I genuinely keep open while I work, not a list copy-pasted from documentation.
This covers the full lifecycle — images, containers, volumes, networks, Compose, and the DevOps habits that separate “I got a container running” from “I run production workloads on this.” Whether you’re debugging a crashing container at 2 a.m. or setting up your first CI pipeline, this is meant to be the page you land on.
Table of Contents
- Docker Basics: Images vs Containers
- Installing and Verifying Docker
- Working with Images
- Running and Managing Containers
- Inspecting and Debugging Containers
- Dockerfile Essentials
- Volumes and Data Persistence
- Networking
- Docker Compose
- Docker Registry and Publishing Images
- Resource Limits and Performance
- Docker Swarm Basics
- Security Best Practices
- Troubleshooting Common Errors
- Real-World DevOps Workflows
- Common Mistakes to Avoid
- FAQs
- Interview Questions
- Printable Quick-Reference Summary
- Official Documentation Links
1. Docker Basics: Images vs Containers
Before the commands, the mental model that made everything click for me: an image is a read-only template — your application plus everything it needs to run, packaged into layers. A container is a running (or stopped) instance of that image. You can spin up ten containers from the same image, and each one runs in its own isolated process with its own filesystem layer on top. Images are the recipe; containers are the meal.
2. Installing and Verifying Docker
| Command | Description |
|---|---|
docker --version | Shows the installed Docker version |
docker info | Displays system-wide Docker info (containers, images, storage driver) |
docker version | Shows client and server (daemon) version details |
docker run hello-world | Runs a test container to confirm Docker is working |
Example:
docker run hello-world
Expected output (abbreviated):
Hello from Docker!
This message shows that your installation appears to be working correctly.
3. Working with Images
| Command | Description |
|---|---|
docker pull <image> | Downloads an image from a registry |
docker pull <image>:<tag> | Pulls a specific tagged version |
docker images | Lists all local images |
docker image ls | Same as above (newer syntax) |
docker build -t <name>:<tag> . | Builds an image from a Dockerfile in the current directory |
docker tag <image> <new-name> | Tags an image with a new name |
docker rmi <image> | Removes a local image |
docker image prune | Removes unused (dangling) images |
docker image prune -a | Removes all unused images, not just dangling ones |
docker history <image> | Shows the layer history of an image |
docker save -o file.tar <image> | Exports an image to a tar file |
docker load -i file.tar | Imports an image from a tar file |
Example:
docker build -t myapp:1.0 .
Expected output (abbreviated):
[+] Building 12.4s (10/10) FINISHED
=> [internal] load build definition from Dockerfile
=> => naming to docker.io/library/myapp:1.0
Real-world tip: Always tag images with a meaningful version instead of relying only on latest. When something breaks in production, latest tells you nothing about what changed.
4. Running and Managing Containers
| Command | Description |
|---|---|
docker run <image> | Creates and starts a new container |
docker run -d <image> | Runs in detached (background) mode |
docker run -it <image> bash | Runs interactively with a terminal attached |
docker run -p 8080:80 <image> | Maps host port 8080 to container port 80 |
docker run --name <name> <image> | Assigns a custom container name |
docker run -e VAR=value <image> | Sets an environment variable |
docker run --rm <image> | Automatically removes the container when it stops |
docker ps | Lists running containers |
docker ps -a | Lists all containers, including stopped ones |
docker start <container> | Starts a stopped container |
docker stop <container> | Gracefully stops a running container |
docker restart <container> | Restarts a container |
docker kill <container> | Forcefully stops a container immediately |
docker rm <container> | Removes a stopped container |
docker rm -f <container> | Force-removes a running container |
docker container prune | Removes all stopped containers |
Example:
docker run -d --name webapp -p 3000:3000 -e NODE_ENV=production myapp:1.0
Expected output:
a1b2c3d4e5f6g7h8i9j0
(This is the container ID — Docker prints it on success.)
5. Inspecting and Debugging Containers
| Command | Description |
|---|---|
docker logs <container> | Shows container logs |
docker logs -f <container> | Streams logs live (like tail -f) |
docker exec -it <container> bash | Opens an interactive shell inside a running container |
docker exec <container> <command> | Runs a one-off command inside a container |
docker inspect <container> | Shows detailed JSON metadata about a container |
docker stats | Shows live CPU, memory, and network usage for running containers |
docker top <container> | Shows running processes inside a container |
docker diff <container> | Shows filesystem changes made since the container started |
docker cp <container>:<path> <host-path> | Copies files from a container to the host |
docker cp <host-path> <container>:<path> | Copies files from the host into a container |
Example:
docker exec -it webapp bash
Expected output: Drops you into an interactive shell inside the container, e.g. root@a1b2c3d4:/app#
Debugging habit I rely on constantly: when a container exits immediately, docker logs <container> is almost always the first place the answer is — before reaching for inspect or anything heavier.
6. Dockerfile Essentials
A Dockerfile is the blueprint Docker uses to build an image, line by line.
Example Dockerfile for a Node.js app:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "server.js"]
| Instruction | Purpose |
|---|---|
FROM | Sets the base image |
WORKDIR | Sets the working directory inside the container |
COPY | Copies files from the host into the image |
ADD | Like COPY, but also supports URLs and auto-extracting archives |
RUN | Executes a command during the build (e.g., installing packages) |
CMD | Default command run when the container starts (overridable) |
ENTRYPOINT | Fixed command that always runs; args are appended, not replaced |
EXPOSE | Documents which port the container listens on |
ENV | Sets an environment variable in the image |
ARG | Defines a build-time-only variable |
USER | Sets the user the container runs as |
VOLUME | Declares a mount point for persistent data |
HEALTHCHECK | Defines how Docker checks if the container is healthy |
Best practice I always follow: order your Dockerfile so things that change least often (like COPY package*.json and RUN npm ci) come before things that change often (like COPY . .). Docker caches layers, so this ordering means most builds only re-run the final steps instead of reinstalling every dependency from scratch.
Multi-stage build example (keeps final images small):
FROM node:20 AS build
WORKDIR /app
COPY . .
RUN npm ci && npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
CMD ["node", "dist/server.js"]
7. Volumes and Data Persistence
Containers are ephemeral by design — anything written inside them disappears when the container is removed, unless you use volumes.
| Command | Description |
|---|---|
docker volume create <name> | Creates a named volume |
docker volume ls | Lists all volumes |
docker volume inspect <name> | Shows volume details |
docker volume rm <name> | Removes a volume |
docker volume prune | Removes all unused volumes |
docker run -v <volume>:<path> <image> | Mounts a named volume into a container |
docker run -v $(pwd):/app <image> | Bind-mounts a host directory into a container |
Example:
docker volume create db-data
docker run -d --name postgres-db -v db-data:/var/lib/postgresql/data postgres:16
Expected output:
db-data
Rule of thumb: use named volumes for persistent application data (databases, uploads), and bind mounts mainly for local development when you want live code changes reflected instantly inside the container.
8. Networking
| Command | Description |
|---|---|
docker network ls | Lists all Docker networks |
docker network create <name> | Creates a custom network |
docker network inspect <name> | Shows network details, including connected containers |
docker network connect <network> <container> | Connects a running container to a network |
docker network disconnect <network> <container> | Disconnects a container from a network |
docker network rm <name> | Removes a network |
docker run --network <name> <image> | Runs a container attached to a specific network |
Example:
docker network create app-network
docker run -d --name api --network app-network myapi:1.0
docker run -d --name db --network app-network postgres:16
Containers on the same custom network can reach each other by container name (api can connect to db at hostname db) — Docker’s built-in DNS handles that automatically, which is much cleaner than hardcoding IP addresses.
9. Docker Compose
Compose is where Docker starts feeling like a real application platform instead of a single-container tool.
Example docker-compose.yml:
version: "3.9"
services:
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
depends_on:
- db
db:
image: postgres:16
environment:
- POSTGRES_PASSWORD=secretpassword
volumes:
- db-data:/var/lib/postgresql/data
volumes:
db-data:
| Command | Description |
|---|---|
docker compose up | Builds and starts all services |
docker compose up -d | Starts services in detached mode |
docker compose down | Stops and removes containers, networks |
docker compose down -v | Also removes named volumes |
docker compose ps | Lists running services |
docker compose logs -f | Streams logs from all services |
docker compose build | Rebuilds images defined in the compose file |
docker compose restart <service> | Restarts a specific service |
docker compose exec <service> bash | Opens a shell inside a running service |
docker compose config | Validates and displays the resolved compose configuration |
Example:
docker compose up -d
Expected output (abbreviated):
[+] Running 3/3
✔ Network myproject_default Created
✔ Container myproject-db-1 Started
✔ Container myproject-web-1 Started
10. Docker Registry and Publishing Images
| Command | Description |
|---|---|
docker login | Logs in to Docker Hub (or another registry) |
docker login <registry-url> | Logs in to a private registry |
docker tag <image> <username>/<repo>:<tag> | Tags an image for pushing |
docker push <username>/<repo>:<tag> | Pushes an image to a registry |
docker pull <username>/<repo>:<tag> | Pulls an image from a registry |
docker logout | Logs out of the current registry |
Example:
docker tag myapp:1.0 sarahdev/myapp:1.0
docker push sarahdev/myapp:1.0
Expected output (abbreviated):
The push refers to repository [docker.io/sarahdev/myapp]
1.0: digest: sha256:a1b2c3... size: 1571
11. Resource Limits and Performance
| Command | Description |
|---|---|
docker run --memory="512m" <image> | Limits container memory usage |
docker run --cpus="1.5" <image> | Limits CPU usage |
docker update --memory="1g" <container> | Updates resource limits on a running container |
docker stats --no-stream | Shows a one-time snapshot of resource usage |
docker system df | Shows disk space used by images, containers, and volumes |
docker system prune | Removes unused containers, networks, and dangling images |
docker system prune -a --volumes | Aggressive cleanup — removes everything unused, including volumes |
I run docker system prune regularly on my dev machine — Docker is notorious for quietly eating disk space with old layers and stopped containers you forgot about.
12. Docker Swarm Basics
Swarm is Docker’s built-in orchestration tool — lighter-weight than Kubernetes, and worth knowing even if your team eventually moves to something bigger.
| Command | Description |
|---|---|
docker swarm init | Initializes a new swarm on the current node |
docker swarm join --token <token> <ip> | Joins a node to an existing swarm |
docker node ls | Lists nodes in the swarm |
docker service create --name <name> <image> | Creates a new service |
docker service ls | Lists running services |
docker service scale <service>=<n> | Scales a service to n replicas |
docker service update --image <image> <service> | Rolling-updates a service to a new image |
docker stack deploy -c <compose-file> <stack-name> | Deploys a full stack from a compose file |
13. Security Best Practices
- Never run containers as root unless there’s a specific reason — add a
USERinstruction in your Dockerfile. - Use official or verified base images where possible, and pin specific versions rather than
latest. - Scan images for vulnerabilities using
docker scoutor tools like Trivy before pushing to production. - Keep secrets out of images and Dockerfiles — use environment variables injected at runtime, Docker secrets, or a secrets manager, never
ENVlines with hardcoded passwords. - Minimize image size and attack surface with multi-stage builds and slim/alpine base images.
- Limit container privileges — avoid
--privilegedmode unless absolutely required, and drop unnecessary Linux capabilities with--cap-drop. - Set resource limits on every production container to prevent one runaway process from starving the host.
- Regularly update base images — a stale base image is one of the most common sources of unpatched vulnerabilities.
- Use read-only filesystems where possible:
docker run --read-only <image>.
14. Troubleshooting Common Errors
| Error | Likely Cause | Fix |
|---|---|---|
Cannot connect to the Docker daemon | Docker service isn’t running | Start Docker Desktop or run sudo systemctl start docker |
port is already allocated | Another process or container is using that host port | Change the host port mapping or stop the conflicting process |
Error response from daemon: No such container | Container name/ID is wrong or already removed | Check docker ps -a for the correct name/ID |
Container exits immediately after docker run | The main process finished or crashed instantly | Check docker logs <container>; ensure CMD/ENTRYPOINT runs a long-lived process |
permission denied on a mounted volume | File permission mismatch between host and container user | Adjust ownership with chown, or match container user ID to host user |
no space left on device | Docker’s storage is full of unused images/layers | Run docker system prune -a --volumes |
| Build is extremely slow | Poor Dockerfile layer ordering, large build context | Reorder Dockerfile, add a .dockerignore file |
.dockerignore example (speeds up builds significantly):
node_modules
.git
*.log
.env
dist
15. Real-World DevOps Workflows
Local development loop:
docker compose up -d
docker compose logs -f web
# make code changes, container picks them up via bind mount
docker compose down
CI/CD build-and-push pipeline (conceptual, e.g., GitHub Actions):
docker build -t sarahdev/myapp:${{ github.sha }} .
docker tag sarahdev/myapp:${{ github.sha }} sarahdev/myapp:latest
docker push sarahdev/myapp:${{ github.sha }}
docker push sarahdev/myapp:latest
Zero-downtime deployment pattern with Swarm:
docker service update --image sarahdev/myapp:new-tag --update-parallelism 1 --update-delay 10s myapp-service
Debugging a production incident:
docker ps
docker logs --tail 200 -f <container>
docker exec -it <container> sh
docker stats <container>
16. Common Mistakes to Avoid
- Running containers as root without a good reason.
- Using
latestas your only tag, making rollbacks and debugging harder. - Not using a
.dockerignorefile, which bloats the build context and slows builds. - Storing persistent data inside the container’s writable layer instead of a volume — losing everything on
docker rm. - Hardcoding secrets or API keys directly into a Dockerfile or image.
- Ignoring image size, leading to slow deploys and larger attack surfaces.
- Forgetting to clean up unused images, containers, and volumes, leading to disk space issues.
- Not setting resource limits, letting one container consume all host resources.
- Using
docker runin production instead of Compose, Swarm, or Kubernetes for anything beyond a quick test.
17. FAQs
Q: What’s the difference between an image and a container? An image is the static, read-only template. A container is a running instance created from that image, with its own writable layer on top.
Q: What’s the difference between CMD and ENTRYPOINT in a Dockerfile? CMD sets a default command that can be fully overridden when running the container. ENTRYPOINT sets a fixed command that always executes, with any extra arguments appended rather than replacing it. They’re often combined: ENTRYPOINT for the fixed binary, CMD for default arguments.
Q: Why did my container exit right after starting? Docker containers stay alive only as long as their main process (PID 1) keeps running. If that process finishes — or was never a long-running process to begin with — the container exits, even if the exit code is 0.
Q: Do I need Kubernetes if I’m already using Docker? Not necessarily. Docker Compose and Swarm handle small to mid-sized deployments fine. Kubernetes becomes worth the added complexity when you need advanced scheduling, multi-node orchestration at scale, or you’re already working in an environment that expects it.
Q: How is a bind mount different from a volume? A bind mount maps a specific host path into the container, so it’s tied to your local filesystem structure. A named volume is managed entirely by Docker, making it more portable and better suited for production data.
Q: Why is my image so large? Common causes are using a full OS base image instead of a slim/alpine variant, not cleaning up build dependencies, or copying unnecessary files because there’s no .dockerignore. Multi-stage builds fix most of this.
Q: Is Docker the same as a virtual machine? No. Containers share the host machine’s kernel and isolate at the process level, making them far lighter and faster to start than a full VM, which virtualizes an entire operating system.
18. Interview Questions
- Explain the difference between an image, a container, and a Dockerfile.
- What’s the purpose of multi-stage builds, and how do they reduce image size?
- How does Docker layer caching work, and how do you optimize a Dockerfile for it?
- Describe the difference between a bind mount and a named volume.
- How would you debug a container that keeps restarting?
- What’s the difference between
docker stopanddocker kill? - How do containers on the same Docker network communicate with each other?
- What security risks come with running a container as root, and how do you avoid it?
- How would you reduce the size of a Node.js or Python Docker image?
- What’s the difference between Docker Compose and Docker Swarm?
19. Printable Quick-Reference Summary
IMAGES
docker pull <image>
docker build -t name:tag .
docker images
docker rmi <image>
CONTAINERS
docker run -d -p host:container --name name image
docker ps -a
docker stop / start / restart <container>
docker rm <container>
DEBUGGING
docker logs -f <container>
docker exec -it <container> bash
docker inspect <container>
docker stats
VOLUMES
docker volume create <name>
docker run -v name:/path image
NETWORKS
docker network create <name>
docker run --network name image
COMPOSE
docker compose up -d
docker compose down
docker compose logs -f
docker compose exec service bash
REGISTRY
docker login
docker tag image user/repo:tag
docker push user/repo:tag
CLEANUP
docker system prune -a --volumes
docker image prune
docker container prune
20. Official Documentation Links
- Docker official documentation: https://docs.docker.com
- Dockerfile reference: https://docs.docker.com/reference/dockerfile
- Docker Compose reference: https://docs.docker.com/compose
- Docker Hub: https://hub.docker.com
- Docker Swarm mode overview: https://docs.docker.com/engine/swarm
That’s the reference I actually use — from docker run hello-world on day one to debugging a service mid-incident years later. Keep it close, and don’t feel like you need to memorize every flag. The commands you use daily will stick on their own; this page is for the ones you only need once every few months and always forget.
