Docker

Complete Docker cheat sheet covering containers, images, Dockerfile, volumes, networking, Docker Compose, and common interview patterns.

7 sections13 cards

How Docker thinks

Docker packages an application and all its dependencies into a standardized unit called a container. The container runs the same everywhere — your laptop, a teammate's machine, a cloud server.

Key concepts:

Image — a read-only blueprint. Built from a Dockerfile. Like a class definition.

Container — a running instance of an image. Like an object instantiated from a class. Isolated process with its own filesystem, network, and process space.

Registry — storage for images. Docker Hub is the default public registry. You can run private registries.

Containers vs VMs: VMs virtualize hardware (heavy, slow to start). Containers share the host OS kernel (lightweight, start in milliseconds).

Images

docker pull nginx — pull image from registry.

docker images — list local images.

docker build -t myapp:1.0 . — build image from Dockerfile in current directory. -t tags the image.

docker build -t myapp -f Dockerfile.prod . — specify a different Dockerfile.

docker rmi image-name — remove image.

docker image prune — remove dangling (untagged) images.

docker tag myapp:1.0 myuser/myapp:1.0 — tag for pushing.

docker push myuser/myapp:1.0 — push to registry.

docker inspect image-name — detailed image metadata.

docker history image-name — show image layers.

Containers

docker run nginx — create and start container from image.

docker run -d nginx — detached (background) mode.

docker run -p 3000:3000 myapp — map host port 3000 to container port 3000.

docker run --name mycontainer nginx — assign a name.

docker run -e NODE_ENV=production myapp — set environment variable.

docker run -v /host/path:/container/path myapp — bind mount a volume.

docker run --rm myapp — auto-remove container when it exits.

docker run -it ubuntu bash — interactive terminal.

docker ps — list running containers. docker ps -a — all including stopped.

docker stop container-name — graceful stop (SIGTERM). docker kill — force (SIGKILL).

docker start container-name — start stopped container.

docker rm container-name — remove stopped container. docker rm -f — force remove running container.

docker logs container-name — view logs. -f to follow. --tail 100 last 100 lines.

docker exec -it container-name bash — open shell inside running container.

docker cp file.txt container:/path — copy file into container.

Dockerfile instructions

FROM node:20-alpine — base image. Always start here. Alpine variants are smaller.

WORKDIR /app — set working directory. Creates it if it doesn't exist. All subsequent commands run from here.

COPY package*.json ./ — copy files from host to image. First arg is source, second is destination.

RUN npm ci — execute command in a new layer. Each RUN creates a layer — combine related commands with && to reduce layers.

COPY . . — copy rest of source code.

EXPOSE 3000 — document which port the app uses. Doesn't actually publish — you still need -p in docker run.

ENV NODE_ENV=production — set environment variable in image.

ARG — build-time variable. Passed with --build-arg. Not available at runtime.

CMD ["node", "server.js"] — default command when container starts. Can be overridden at runtime. Use JSON array form (exec form) not shell form.

ENTRYPOINT ["node"] — like CMD but not overridable (only appendable). CMD becomes default arguments to ENTRYPOINT.

USER node — run as non-root user. Security best practice.

HEALTHCHECK CMD curl -f http://localhost:3000/health || exit 1 — define health check.

Multi-stage builds

Use multiple FROM statements. Each is a stage. Copy artifacts between stages. Final image only contains what you explicitly copy — no build tools, no dev dependencies.

FROM node:20 AS builder

WORKDIR /app

COPY . .

RUN npm ci && npm run build

FROM node:20-alpine AS runner

WORKDIR /app

COPY --from=builder /app/dist ./dist

COPY --from=builder /app/node_modules ./node_modules

CMD ["node", "dist/server.js"]

Multi-stage builds are essential for production. A Next.js app's builder image might be 2GB. The runner image should be under 200MB.

Dockerfile best practices

Order layers from least to most frequently changing. Put COPY package.json + RUN npm install before COPY . . — Docker caches the install layer and only re-runs it when package.json changes.

.dockerignore — exclude files from build context. Always include: node_modules, .git, .env, dist. Smaller context = faster builds.

Use specific base image versions — node:20.11-alpine3.19 not node:latest. Reproducible builds.

Run as non-root: USER node before CMD. Many base images have a non-root user pre-created.

One process per container — don't run your app + database + nginx in one container. Use Docker Compose for multi-service setups.

Volume types

Bind mount — maps a specific host path to container path. -v /host/path:/container/path. Host and container share the same files. Changes reflect instantly on both sides. Use for development.

Named volume — Docker-managed storage. -v mydata:/container/path. Persists across container restarts. Best for databases and persistent data in production.

tmpfs mount — in-memory only. Not persisted. Fast. Use for sensitive data that shouldn't touch disk.

docker volume create mydata — create named volume.

docker volume ls — list volumes.

docker volume inspect mydata — see where data is stored on host.

docker volume rm mydata — remove volume. docker volume prune — remove unused volumes.

Docker networks

Containers on the same network can reach each other by container name — Docker has built-in DNS.

Network drivers:

bridge (default) — isolated network on the host. Containers can communicate within the network.

host — container shares host network stack. No isolation. Maximum performance.

none — no networking. Fully isolated.

overlay — multi-host networking. Used with Docker Swarm.

docker network create mynet — create network.

docker run --network mynet myapp — connect container to network.

docker network connect mynet container — connect running container to network.

docker network ls docker network inspect mynet

Compose basics

Docker Compose defines and runs multi-container applications with a single YAML file. The modern command is docker compose (v2, built-in) not docker-compose (v1, separate binary).

docker compose up — create and start all services.

docker compose up -d — detached mode.

docker compose up --build — rebuild images before starting.

docker compose down — stop and remove containers and networks. --volumes also removes volumes.

docker compose ps — list service containers.

docker compose logs -f service-name — follow logs for a service.

docker compose exec service-name bash — shell into a running service.

docker compose run service-name command — run one-off command in a new container.

docker compose restart service-name — restart a specific service.

compose.yaml structure

A typical Node + MongoDB setup:

services:

app:

build: . — build from Dockerfile in current directory.

ports: ['3000:3000']

environment: [NODE_ENV=production]

env_file: [.env] — load from file.

depends_on: [mongo] — start mongo before app. Does not wait for mongo to be ready — use healthchecks for that.

volumes: ['./src:/app/src'] — bind mount for dev.

mongo:

image: mongo:7

volumes: ['mongodata:/data/db']

volumes:

mongodata: — declare named volume.

Services on the same Compose project are on the same default network automatically — app can reach mongo using the hostname mongo.

Multiple compose files

compose.yaml — base config.

compose.override.yaml — automatically merged when you run docker compose up. Use for dev overrides.

docker compose -f compose.yaml -f compose.prod.yaml up — explicitly merge files. Production config overrides base.

Common pattern: base has shared service definitions. Override adds dev volumes and ports. Prod file sets resource limits and prod image tags.

Common traps

EXPOSE doesn't publish ports — it's documentation only. You still need -p host:container in docker run or ports: in compose.

CMD vs ENTRYPOINT — CMD is the default command, overridable. ENTRYPOINT is the executable, CMD becomes its default args. If both are set, CMD provides default args to ENTRYPOINT.

Layer caching — COPY and RUN create new layers. If a layer changes, all subsequent layers are invalidated. Order your Dockerfile to maximize cache hits.

Container stops when PID 1 exits — your app must run as the foreground process. Don't use shell scripts that background the process.

depends_on only waits for the container to start, not for the service inside to be ready. Use healthcheck + depends_on: condition: service_healthy for proper ordering.

Running as root — containers run as root by default. This is a security risk. Always set a non-root USER in your Dockerfile.

Things to know cold

Image layers are cached and shared between images. If two images share the same base, the base layer is stored once. This is why alpine base images matter — smaller layers, faster pulls.

Container filesystem is ephemeral — any data written inside a container is lost when the container is removed. Use volumes for persistence.

Environment variables in Docker: -e KEY=val at runtime, ENV KEY=val in Dockerfile (baked in), env_file in compose (from file). Runtime values override Dockerfile values.

Docker build context — when you run docker build ., the entire current directory is sent to the Docker daemon. A good .dockerignore is critical for build speed.

docker system prune — remove all stopped containers, dangling images, unused networks. Add -a to also remove unused images. --volumes to include volumes. Use carefully.