Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

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; --rm deletes 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 .env loads many — values are visible in docker inspect
docker run -v $PWD:/app
bind-mount the current directory; -v data:/var/lib/x mounts a named volume
docker run -w /app
set the working directory; -u 1000:1000 runs 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.5 caps CPU
docker run --init
add a tiny init as PID 1 so signals are forwarded and zombies reaped
docker run --platform
linux/amd64 on an Apple Silicon Mac runs emulated — expect it to be slow
docker stop web
SIGTERM, then SIGKILL after 10 s; docker rm -f web kills and removes at once

Inspect & debug

docker ps · ps -a
running containers · including stopped ones
docker logs -f web
follow logs; --tail 100 limits history, --since 10m filters by time
docker exec -it web sh
shell into a running container (bash if it has one); -u root as 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: A added, C changed, D deleted
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 build stops at one stage of a multi-stage file
docker build --no-cache
rebuild every layer — when a cached apt-get update layer has gone stale
docker build --build-arg
pass ARG values; never secrets, they persist in the image history
docker buildx build
multi-arch: --platform linux/amd64,linux/arm64 with --push builds and pushes both
docker images
list local images; docker rmi app:1.0 removes 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 chown layer
ARG vs ENV
ARG exists only during the build; ENV persists into the running container
EXPOSE 8080
documentation only — it publishes nothing; -p or -P does
HEALTHCHECK
a command Docker runs periodically; the result shows as (healthy) in docker 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; --build rebuilds images first
docker compose down -v
remove containers and networks; -v also deletes volumes — your data
docker compose build web
rebuild one service; docker compose up -d --build web rebuilds 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 cmd starts a one-off
docker compose ps
services, their state and ports for this project
docker compose config
print the merged, interpolated file — check what .env and overrides produced
docker compose pull
fetch newer images, then up -d recreates only the changed services
depends_on
start order only; add condition: service_healthy to 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; --volumes deletes 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.0

The 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 prune

See 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 postgres

The 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.