Docker Survival Guide
The container commands you reach for daily — build, run, debug, clean up — plus the Dockerfile lines that actually matter.
An image is a read-only stack of filesystem layers plus a default command. A container is a running or stopped instance of one, with a thin writable layer on top that disappears when the container is removed, so anything that must outlive it belongs in a volume or a bind mount. Each Dockerfile instruction adds a layer, and Docker reuses cached layers until an input changes, which is why instruction order decides build speed.
Flags go before the image name: docker run [flags] image [command]. Anything after the image is passed to the container.
Run containers
- docker run -it --rm
- interactive throwaway:
docker run -it --rm alpine sh;--rmdeletes it on exit - docker run -d -p 8080:80
- detached, host port 8080 → container port 80: add
--name web nginx - docker run -e K=V
- set an env var;
--env-file .envloads many — values are visible indocker inspect - docker run -v $PWD:/app
- bind-mount the current directory;
-v data:/var/lib/xmounts a named volume - docker run -w /app
- set the working directory;
-u 1000:1000runs as that user and group - docker run --entrypoint
- replace the image’s ENTRYPOINT and drop its CMD:
-it --entrypoint sh img - --restart unless-stopped
- restart after crashes and daemon reboots, unless you stopped it yourself
- docker run --memory 512m
- cap memory (OOM-killed above it);
--cpus 1.5caps CPU - docker run --init
- add a tiny init as PID 1 so signals are forwarded and zombies reaped
- docker run --platform
linux/amd64on an Apple Silicon Mac runs emulated — expect it to be slow- docker stop web
- SIGTERM, then SIGKILL after 10 s;
docker rm -f webkills and removes at once
Inspect & debug
- docker ps · ps -a
- running containers · including stopped ones
- docker logs -f web
- follow logs;
--tail 100limits history,--since 10mfilters by time - docker exec -it web sh
- shell into a running container (
bashif it has one);-u rootas root - docker inspect web
- full JSON: IP, mounts, env;
-f "{{.State.Status}}"extracts one field - docker stats --no-stream
- one snapshot of CPU, memory and network per container; drop the flag to watch live
- docker top web
- processes inside the container, as the host sees them
- docker cp web:/app/x .
- copy files out of (or into) a container, running or stopped
- docker diff web
- files changed versus the image:
Aadded,Cchanged,Ddeleted - docker events
- live stream of daemon events: starts, deaths, OOM kills, health changes
- docker port web
- which host ports map to the container’s ports
Images & build
- docker build -t app .
- build and tag from the Dockerfile here; the final
.is the context sent to the daemon - docker build -f FILE .
- use another Dockerfile;
--target buildstops at one stage of a multi-stage file - docker build --no-cache
- rebuild every layer — when a cached
apt-get updatelayer has gone stale - docker build --build-arg
- pass
ARGvalues; never secrets, they persist in the image history - docker buildx build
- multi-arch:
--platform linux/amd64,linux/arm64with--pushbuilds and pushes both - docker images
- list local images;
docker rmi app:1.0removes one - docker tag · docker push
- name an image for a registry, then upload:
docker tag app:1.0 reg/app:1.0 - docker pull img@sha256:…
- pin by digest — a tag can be moved to new content, a digest cannot
- docker history app:1.0
- layers and their sizes — find what bloated the image
- docker save · load
- image to a tar file and back — moving images to air-gapped hosts
Dockerfile essentials
- FROM
- Base image. Pin a tag/digest; prefer
-slim/alpine. - Layer caching
- Copy
package.json& install before copying source, so deps cache. - Multi-stage
FROM node AS build…FROM nginx+COPY --from=build— tiny final image.- CMD vs ENTRYPOINT
- ENTRYPOINT = the executable; CMD = default args. Use exec form
["node","x.js"]. - .dockerignore
- Exclude
node_modules,.git— shrinks build context. - USER app
- never ship a container running as root; create the user in an earlier
RUN - RUN a && b
- chain steps in one layer and clean caches in the same
RUN, or the bytes stay in the image - WORKDIR /app
- directory for later instructions, created if missing — no
RUN cd - COPY --chown=app
- copy files already owned by the runtime user — no extra
chownlayer - ARG vs ENV
ARGexists only during the build;ENVpersists into the running container- EXPOSE 8080
- documentation only — it publishes nothing;
-por-Pdoes - HEALTHCHECK
- a command Docker runs periodically; the result shows as
(healthy)indocker ps - RUN --mount=type=cache
- BuildKit cache for package managers, e.g.
target=/root/.cache/pip— fast rebuilds - RUN --mount=type=secret
- use a secret during the build without it landing in any layer (BuildKit)
Volumes & networks
- docker volume ls · rm
- named volumes outlive containers — this is where databases keep their data
- docker network create
- user-defined network: containers on it reach each other by name, e.g.
db:5432 - --network host
- share the host’s network stack (fully supported on Linux) — no port mapping needed
- host.docker.internal
- reach the host from a container (Docker Desktop; on Linux add it with
--add-host)
Compose
- docker compose up -d
- start the stack in the background;
--buildrebuilds images first - docker compose down -v
- remove containers and networks;
-valso deletes volumes — your data - docker compose build web
- rebuild one service;
docker compose up -d --build webrebuilds and restarts it - docker compose logs -f
- follow every service’s logs; name a service to filter
- docker compose exec web
- run in a live service: append
sh;run --rm web cmdstarts a one-off - docker compose ps
- services, their state and ports for this project
- docker compose config
- print the merged, interpolated file — check what
.envand overrides produced - docker compose pull
- fetch newer images, then
up -drecreates only the changed services - depends_on
- start order only; add
condition: service_healthyto wait for readiness
Clean up
- docker system df
- disk used by images, containers, volumes and build cache
- docker system prune
- remove stopped containers, unused networks, dangling images and build cache
- docker system prune -af
- also every unused image;
--volumesdeletes unused volumes too (careful) - docker image prune -a
- remove every image no container uses — usually frees the most disk
- docker builder prune
- clear the BuildKit cache, often the biggest hidden disk hog
Recipes
Debug a container that exits immediately
docker ps -a --filter status=exited
docker logs web
docker run -it --rm --entrypoint sh app:1.0The logs usually say why it died; if they are empty, start the same image with a shell instead of its entrypoint and run the command by hand. Exit code 137 means it was killed, often by the OOM killer.
A small, cache-friendly production image (Node)
FROM node:20-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-slim
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
USER node
CMD ["node", "dist/server.js"]Dependencies install before the source is copied, so code edits reuse the cached npm ci layer, and the final stage ships no build tools. The official Node images already include a node user.
Reclaim disk space safely
docker system df
docker container prune
docker image prune -a
docker builder pruneSee what is using space, then prune in increasing order of risk. Leave volumes alone unless you are sure: that is where databases keep their data.
Run a throwaway Postgres for local development
docker volume create pgdata
docker run -d --name pg -e POSTGRES_PASSWORD=dev -p 5432:5432 -v pgdata:/var/lib/postgresql/data postgres:16
docker exec -it pg psql -U postgresThe named volume keeps the data when the container is replaced: docker rm -f pg removes the container but not pgdata.
Build one image tag for Intel and ARM servers
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/app:1.0 --push .Each host pulls the variant for its own architecture. Pushing straight to the registry is the simple path, because loading a multi-platform image locally needs the containerd image store.
Questions about Docker Survival Guide Cheat Sheet
What is the difference between CMD and ENTRYPOINT in a Dockerfile?
ENTRYPOINT sets the executable a container always runs, and CMD supplies its default arguments, or the whole default command when there is no ENTRYPOINT. Arguments after the image name in docker run replace CMD but not ENTRYPOINT, so an image with ENTRYPOINT ["python", "app.py"] and CMD ["--port", "8000"] runs on port 8000 unless you pass other flags. Use the JSON array form for both: the shell form wraps the command in sh -c, so your process is not PID 1 and never sees SIGTERM, which turns docker stop into a 10-second wait and a kill.
What is the difference between a Docker image and a container?
An image is a read-only template: a stack of filesystem layers plus metadata such as the default command, environment and exposed ports. A container is a running or stopped instance of an image, with its own writable layer, processes, network identity and the configuration given to docker run. You can start many containers from one image, and removing a container deletes only its writable layer, never the image. That is why data written inside a container disappears with it unless it went to a volume or a bind mount.
How do I reduce the size of a Docker image?
Start from a smaller base, such as a slim or alpine variant, or a distroless image for the final stage. Use a multi-stage build so compilers, dev dependencies and source code stay in the build stage and only the output is copied into the final one. Combine installs and cleanup in a single RUN so package caches never land in a layer, and add a .dockerignore so the build context leaves out node_modules, .git and local artifacts. Then run docker history to see which layers are still large.
Why can’t my container reach a service on localhost?
Inside a container, localhost is the container itself, not your machine. To reach a service on the host, use host.docker.internal, which Docker Desktop provides; on Linux, add --add-host=host.docker.internal:host-gateway to docker run. Containers that talk to each other should share a user-defined network, created with docker network create or automatically by Compose, and address each other by container or service name, such as db:5432. Also check that the service inside a container listens on 0.0.0.0 rather than 127.0.0.1, or nothing outside it can connect.