Cheat Sheets / Containers & Docker

Docker & Containers cheat sheet

160 commands from every lesson of Docker & Containers — Level by Level, on one page.

01 · What a container really is

Run & look around

docker run -d --name web -p 8080:80 nginx:1.27Start a container in the background, publish port 8080
docker ps / docker ps -aRunning containers / all containers, including stopped
docker logs -f --tail 50 webFollow the last 50 log lines
docker exec -it web shOpen a shell inside a running container
docker inspect webFull JSON: config, IPs, mounts, state
docker inspect -f '{{.State.Status}} {{.State.ExitCode}}' webPick one field with a Go template
docker stats --no-streamCPU, memory and I/O per container
docker top webProcesses inside a container

Lifecycle

docker stop web && docker start webGraceful stop (SIGTERM, then SIGKILL after 10 s) and start
docker restart webStop + start
docker rm -f webRemove a container (force-stops it first)
docker run --rm -it alpine:3.20 shThrowaway interactive container, removed on exit

Engine info

docker versionClient and server (daemon) versions
docker infoStorage driver, data root, cgroup driver, registries
systemctl status dockerIs the daemon running?
journalctl -u docker -n 100Daemon logs

02 · Images & Dockerfiles

Build

docker build -t shop/api:1.0 .Build from the Dockerfile in the current folder
docker build -f docker/Dockerfile.prod -t shop/api:1.0 .Use a Dockerfile with another name or path
docker build --no-cache -t shop/api:1.0 .Ignore the cache (fresh base layers, fresh package installs)
docker build --target builder -t shop/api:build .Stop at one stage of a multi-stage build
docker build --build-arg VERSION=1.4 .Pass a build argument (ARG)
docker buildx build --platform linux/amd64,linux/arm64 -t reg/app:1 --push .Multi-architecture build, pushed to a registry

Inspect images

docker images / docker image lsLocal images and sizes
docker history shop/api:1.0Layers and the instruction that made each one
docker image inspect shop/api:1.0Config: CMD, ENTRYPOINT, USER, ENV, labels
docker image pruneRemove dangling (untagged) images

Dockerfile instructions

FROM image:tag AS nameBase image; name a stage for multi-stage builds
COPY --from=builder /out/app /appCopy files from an earlier stage
RUN cmdRun at build time; creates a layer
ENTRYPOINT ["/app"] + CMD ["--port", "8080"]Fixed program + default arguments
USER 10001Run as a non-root user
HEALTHCHECK CMD curl -f http://localhost:8080/healthz || exit 1Container health reported by Docker

03 · Networking & storage

Networks

docker network create shopnetUser-defined bridge network (with DNS by container name)
docker run -d --name db --network shopnet postgres:16Attach a container to a network
docker network connect shopnet webAttach a running container to another network
docker network inspect shopnetSubnet, gateway and attached containers
docker port webWhich host ports are published
docker run -p 127.0.0.1:8080:80 nginxPublish only on localhost, not every interface
docker run --network host nginxShare the host's network stack (no isolation, no -p)

Storage

docker volume create pgdataNamed volume managed by Docker
docker run -v pgdata:/var/lib/postgresql/data postgres:16Mount a named volume
docker run -v "$PWD/conf":/etc/nginx/conf.d:ro nginxBind-mount a host folder, read-only
docker run --mount type=tmpfs,target=/tmp appIn-memory scratch space
docker volume ls / docker volume inspect pgdataList volumes / where the data lives
docker volume pruneRemove volumes not used by any container (careful)

Back up a volume

docker run --rm -v pgdata:/data -v "$PWD":/backup alpine tar czf /backup/pgdata.tgz -C /data .Tar a volume's contents to the current folder

04 · Docker Compose basics

Everyday commands

docker compose up -dCreate and start everything in the background
docker compose up -d --buildRebuild images that have a build: section, then start
docker compose psServices, state and ports for this project
docker compose logs -f apiFollow one service's logs
docker compose exec db psql -U appRun a command in a running service
docker compose run --rm api python manage.py migrateOne-off command in a new container
docker compose restart apiRestart one service
docker compose downStop and remove containers and networks (volumes kept)
docker compose down -vAlso delete named volumes: data is lost

Check and debug

docker compose configShow the final merged file with variables filled in
docker compose pullPull newer images for all services
docker compose topProcesses in each service
docker compose -p shop -f compose.yaml up -dSet the project name and file explicitly

05 · Compose for real environments

Health & order

docker compose psShows (healthy) / (unhealthy) per service
docker inspect --format '{{json .State.Health}}' shop-db-1Health-check history and output
docker compose up -d --waitReturn only when services are running/healthy

Files & profiles

docker compose -f compose.yaml -f compose.prod.yaml up -dMerge a base file with an override
docker compose --profile debug up -dAlso start services in the debug profile
docker compose configSee the merged result before applying it

Keep it tidy

docker compose logs --since 10m apiRecent logs of one service
docker compose imagesImages used by the project
docker compose pull && docker compose up -dUpdate to newer image tags / digests

06 · Registries: tag, push & pull

Tag, push, pull

docker tag shop/api:1.4 registry.lab.local:5000/shop/api:1.4Add a name that points at another registry
docker push registry.lab.local:5000/shop/api:1.4Upload the image (only missing layers are sent)
docker pull registry.lab.local:5000/shop/api:1.4Download it on another host
docker pull nginx@sha256:<digest>Pull an exact, immutable image by digest
docker images --digestsShow local images with their digests
docker buildx imagetools inspect nginx:1.27Digest and platforms of a remote image

Log in

docker login registry.lab.local:5000Log in to a private registry (prompts for password)
echo "$TOKEN" | docker login -u ci-bot --password-stdin ghcr.ioNon-interactive login for CI
aws ecr get-login-password --region eu-west-1 | docker login -u AWS --password-stdin <acct>.dkr.ecr.eu-west-1.amazonaws.comLog in to Amazon ECR
docker logout registry.lab.local:5000Remove stored credentials
cat ~/.docker/config.jsonWhere logins are stored (check for credsStore)

Save / load without a registry

docker save -o api-1.4.tar shop/api:1.4Export an image to a tar file
docker load -i api-1.4.tarImport it on an air-gapped host

07 · Run your own private registry

Certificate & password

openssl req -x509 -newkey rsa:4096 -sha256 -days 365 -nodes -keyout certs/domain.key -out certs/domain.crt -subj "/CN=registry.lab.local" -addext "subjectAltName=DNS:registry.lab.local,IP:192.168.56.10"Self-signed certificate with SANs (lab)
docker run --rm --entrypoint htpasswd httpd:2 -Bbn ci-bot 'S3cret!' > auth/htpasswdCreate a bcrypt htpasswd entry
openssl x509 -in certs/domain.crt -noout -text | grep -A1 'Subject Alternative Name'Check the SANs in a certificate

Registry API

curl -u ci-bot https://registry.lab.local:5000/v2/_catalogList repositories
curl -u ci-bot https://registry.lab.local:5000/v2/shop/api/tags/listList tags of one repository
curl -u ci-bot -I -H 'Accept: application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.v2+json' https://registry.lab.local:5000/v2/shop/api/manifests/1.4Get the manifest digest (Docker-Content-Digest header)

Clean up

curl -u ci-bot -X DELETE https://registry.lab.local:5000/v2/shop/api/manifests/sha256:<digest>Delete a manifest (needs delete enabled)
docker compose exec registry registry garbage-collect --delete-untagged /etc/docker/registry/config.ymlFree the disk space of deleted images (registry:2 path; registry:3 uses /etc/distribution/config.yml)

08 · Configuring the daemon: daemon.json

Edit safely

sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bakBack up before changing
sudo dockerd --validate --config-file /etc/docker/daemon.jsonCheck the file without starting a daemon (Engine 23.0+)
python3 -m json.tool /etc/docker/daemon.jsonQuick JSON syntax check on older engines
sudo systemctl reload dockerApply reloadable settings (SIGHUP) without stopping containers
sudo systemctl restart dockerApply everything (containers stop unless live-restore is on)

Verify

docker infoLogging driver, data root, mirrors, insecure registries, cgroup driver
docker info -f '{{.LoggingDriver}} {{.DockerRootDir}}'One or two fields
docker info -f '{{json .RegistryConfig}}' | python3 -m json.toolMirrors and insecure registries in effect
journalctl -u docker -n 50 --no-pagerWhy the daemon failed to start
systemctl cat dockerThe unit file and drop-ins (look for -H or --config flags)

09 · Registry certificates & the insecure bypass

Diagnose

openssl s_client -connect registry.lab.local:5000 -servername registry.lab.local -showcerts </dev/nullSee the certificate chain the registry actually sends
openssl x509 -in ca.crt -noout -subject -issuer -enddate -ext subjectAltNameWho issued it, when it expires, which names it covers
curl -v https://registry.lab.local:5000/v2/Does the host's trust store accept it?
docker info -f '{{json .RegistryConfig.IndexConfigs}}'Which registries the daemon treats as insecure

Trust one registry (Docker, no restart)

sudo mkdir -p /etc/docker/certs.d/registry.lab.local:5000Folder name = host:port exactly as in image names
sudo cp ca.crt /etc/docker/certs.d/registry.lab.local:5000/ca.crtCA (or self-signed cert) that signed the registry cert
sudo cp client.cert client.key /etc/docker/certs.d/registry.lab.local:5000/Client certificate for mutual TLS

Trust a CA system-wide (restart Docker after)

sudo cp corp-ca.crt /usr/local/share/ca-certificates/ && sudo update-ca-certificatesDebian / Ubuntu
sudo cp corp-ca.crt /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trustRHEL / Rocky / Fedora
sudo cp corp-ca.crt /etc/pki/trust/anchors/ && sudo update-ca-certificatesSUSE / SLES

Bypass (labs only)

"insecure-registries": ["registry.lab.local:5000"]daemon.json: skip verification / allow HTTP for this registry, then reload
podman pull --tls-verify=false registry.lab.local:5000/app:1Podman, one command
skip_verify = truecontainerd hosts.toml, per registry host

10 · Security & troubleshooting

Least privilege

docker run --user 10001:10001 appRun as a non-root UID
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE appDrop every capability, add back only what's needed
docker run --read-only --tmpfs /tmp appRead-only root filesystem with a writable /tmp
docker run --security-opt no-new-privileges appBlock privilege escalation through setuid binaries
docker run --memory 512m --cpus 1 --pids-limit 200 appResource limits

Scan

trivy image shop/api:1.4List known CVEs in OS packages and app dependencies
trivy image --severity HIGH,CRITICAL --exit-code 1 shop/api:1.4Fail a CI step on serious findings
trivy fs --scanners secret .Look for committed secrets in the source

Troubleshoot

docker ps -a --format '{{.Names}}\t{{.Status}}'Exit codes at a glance
docker inspect -f '{{.State.ExitCode}} {{.State.OOMKilled}} {{.State.Error}}' appWhy it stopped
docker events --since 30mWhat the daemon did recently
sudo ss -ltnp | grep :8080Who owns a port
docker system df -vWhat uses the disk
docker system pruneRemove stopped containers, unused networks, dangling images and build cache
docker image prune -a --filter until=168hRemove unused images older than 7 days
docker run --rm --network container:app nicolaka/netshootNetwork tools inside another container's namespace

11 · Capstone: ship an app through a private registry

Build host

docker build -t registry.lab.local:5000/shop/api:1.0 .Build and name it for the registry in one step
docker login registry.lab.local:5000Log in (credentials go to ~/.docker/config.json)
docker push registry.lab.local:5000/shop/api:1.0Push; note the digest it prints

Run host

sudo cp domain.crt /etc/docker/certs.d/registry.lab.local:5000/ca.crtTrust the registry
sudo dockerd --validate --config-file /etc/docker/daemon.json && sudo systemctl restart dockerApply host settings
docker compose -f compose.yaml -f compose.prod.yaml up -d --waitStart the stack and wait for health
docker compose ps && curl -s localhost:8080/healthzVerify

12 · Project: Python front end + back end

Helper script

./app.sh upBuild both images and start them with Compose, wait until healthy
./app.sh runSame with plain docker: network, volume, two docker run commands
./app.sh expose --openShow the URLs for other machines and open the host firewall port
./app.sh testHealth check, add a quote, list quotes through the front end
./app.sh down / ./app.sh cleanStop (keep data) / remove everything
WEB_PORT=9090 BIND_ADDR=127.0.0.1 ./app.sh upAnother port, reachable only from this machine

By hand

docker build -t quotes-backend:1.0 ./backendBuild the back end
docker network create quotes-netNetwork with DNS by container name
docker run -d --name quotes-backend --network quotes-net --network-alias backend -v quotes-data:/data quotes-backend:1.0Back end: no -p, reachable only on the network
docker run -d --name quotes-frontend --network quotes-net -p 8080:8080 quotes-frontend:1.0Front end: the only published port
docker run --rm --network quotes-net curlimages/curl -s http://quotes-frontend:8080/api/quotesTest from inside the network

13 · Project: .NET front end + back end (Linux & Windows)

Linux containers

./app.sh upBuild and start both containers with Compose, wait until healthy
./app.sh runSame with plain docker run
./app.sh expose --openURLs for other machines; open ufw/firewalld
./app.sh test / ./app.sh cleanSmoke test / remove everything

Windows containers (PowerShell)

docker info --format '{{.OSType}}'windows = Windows-containers mode
& $Env:ProgramFiles\Docker\Docker\DockerCli.exe -SwitchDaemonSwitch Docker Desktop between Linux and Windows containers
.\app.ps1 upBuild from Dockerfile.windows and start with compose.windows.yaml
.\app.ps1 expose -OpenAdd an inbound Windows Firewall rule (as Administrator)
docker compose -f compose.windows.yaml up -d --buildThe same without the helper
docker run --isolation=hyperv ...Run an image built for another Windows version in a lightweight VM

.NET image details

mcr.microsoft.com/dotnet/sdk:10.0Build stage: SDK (large, never shipped)
mcr.microsoft.com/dotnet/aspnet:10.0Run stage: ASP.NET Core runtime only
USER $APP_UIDThe non-root 'app' user built into .NET 8+ Linux images
ASPNETCORE_HTTP_PORTS=8080Default listening port in .NET 8+ images