Home DevOps Docker CLI Commands Reference: The Only Guide You'll Need to Ship Containers Without Regret
Beginner 7 min · July 11, 2026

Docker CLI Commands Reference: The Only Guide You'll Need to Ship Containers Without Regret

Docker CLI commands explained from zero.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
Production
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
Before you start⏱ 35 min
  • A terminal
  • Docker installed (any recent version)
  • Willingness to break things in a sandbox
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer

To run a container: docker run nginx. To list running containers: docker ps. To stop one: docker stop . That's the bare minimum to get started.

✦ Definition~90s read
What is Docker CLI Commands Reference?

The Docker CLI is your command-line interface to the Docker daemon. It lets you build, run, stop, and manage containers and images. Every command maps to a REST API call under the hood.

Think of Docker CLI as a remote control for a container factory.
Plain-English First

Think of Docker CLI as a remote control for a container factory. You press 'build' to assemble a container from a recipe (Dockerfile). You press 'run' to start it. You press 'ps' to see which containers are running on the factory floor. You press 'stop' to shut one down. The CLI is your interface to the factory manager (the Docker daemon).

I've seen a single docker run without --restart take down a payment processing pipeline at 2am. The container crashed, nobody noticed, and 14,000 transactions got lost. The Docker CLI is powerful — but it's also sharp. One wrong flag and you're debugging at 3am.

This guide isn't a man page. It's the survival kit I wish I had when I started. We'll cover every command you'll actually use in production, the flags that matter, and the mistakes that will burn you. By the end, you'll be able to navigate Docker CLI like a senior engineer — not just run docker ps and pray.

You'll learn how to build images that don't bloat, run containers that survive crashes, debug networking when containers can't talk, and clean up disk space without accidentally deleting production data. Every command comes with a real scenario, not a toy example.

Why You Need the Docker CLI — Containers Are Not VMs

Before Docker, deploying software meant fighting with dependency hell. 'It works on my machine' was the punchline of every deployment. Docker containers package your app with its entire runtime — libraries, config, binaries — into a single image. The CLI is how you control that image lifecycle.

Without the CLI, you're stuck with GUI tools that hide what's really happening. When a container crashes at 3am, you need commands, not click-ops. The CLI gives you speed, precision, and scriptability.

Here's the mental model: Docker CLI talks to the Docker daemon (dockerd) via a REST API. The daemon manages images, containers, networks, and volumes. Every command you run is a structured request to that daemon. Understanding this helps when debugging — if the CLI hangs, check if the daemon is alive (systemctl status docker).

check-docker-status.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Check if Docker daemon is running
systemctl status docker

# Or for non-systemd systems
docker info

# If daemon is down, start it
sudo systemctl start docker

# Verify with a simple command
docker run --rm hello-world
Output
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2024-01-15 10:23:45 UTC; 2h 30min ago
...
Hello from Docker!
This message shows that your installation appears to be working correctly.
💡Senior Shortcut:
Alias docker ps to dps and docker images to dimg. Saves thousands of keystrokes over a career.
docker-cli-commands-reference THECODEFORGE.IO Docker Container Architecture Layers Component hierarchy from host OS to application Host OS Linux Kernel | Docker Engine Container Runtime containerd | runc Docker Daemon API Server | Image Store | Volume Manager Container Instances Web Server | Database | Cache Networking Bridge Network | Overlay Network Data Persistence Bind Mounts | Volumes THECODEFORGE.IO
thecodeforge.io
Docker Cli Commands Reference

Image Management: Pull, Build, and Tag Like a Pro

Images are the blueprints for containers. You pull them from a registry (like Docker Hub) or build them from a Dockerfile. The most common rookie mistake? Using latest tag in production. latest is a moving target — you'll deploy different code on different days without realizing it.

Always pin to a specific version tag: nginx:1.25.3 not nginx:latest. When you build, tag with a version or commit hash: docker build -t myapp:v1.2.3 .. This gives you reproducible deployments.

Another trap: bloated images. Every RUN command in a Dockerfile adds a layer. Combine commands with && to reduce layers. Use .dockerignore to exclude node_modules, .git, and other junk. A lean image is faster to pull and more secure.

DockerfileDOCKERFILE
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# io.thecodeforge — DevOps tutorial

FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]
Output
Successfully built a1b2c3d4e5f6
Successfully tagged myapp:1.0.0
⚠ Never Do This:
Using docker build --no-cache in CI every time. It rebuilds all layers from scratch, wasting time. Only use it when you suspect a cached layer is stale (e.g., after a base image update).

Running Containers: The Flags That Matter

docker run is the Swiss Army knife. But with 50+ flags, it's easy to miss the ones that keep your container alive in production. The four you must know: -d (detach), --restart, -p (port mapping), and -v (volume mount).

Without --restart, if your app crashes, the container dies and stays dead. Always use --restart unless-stopped for long-running services. It restarts the container unless you explicitly stop it.

Port mapping: -p 8080:80 maps host port 8080 to container port 80. Never expose container ports directly to the internet without a reverse proxy. Use Docker networks for inter-container communication.

Volumes: -v /host/path:/container/path persists data. Without volumes, container data vanishes when the container is removed. For databases, always use named volumes: -v mydata:/var/lib/mysql.

run-nginx.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Run nginx with restart policy, port mapping, and a volume for static files
docker run -d \
  --name web \
  --restart unless-stopped \
  -p 8080:80 \
  -v /home/user/html:/usr/share/nginx/html:ro \
  nginx:1.25.3

# Verify it's running
docker ps --filter "name=web"
Output
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 nginx:1.25.3 "/docker-entrypoint.…" 5 seconds ago Up 4 seconds 0.0.0.0:8080->80/tcp web
🔥Production Trap:
Using --restart always instead of unless-stopped. If you stop the container intentionally (e.g., for maintenance), always will restart it immediately. unless-stopped respects your manual stop.
Docker Volumes vs Bind Mounts Trade-offs for data persistence in containers Docker Volumes Bind Mounts Management Managed by Docker Managed by user Portability Easy to backup/migrate Tied to host path Performance Native performance Slightly slower on macOS/Windows Use Case Persistent database data Development hot-reload Security Isolated from host Direct host access THECODEFORGE.IO
thecodeforge.io
Docker Cli Commands Reference

Container Lifecycle: Stop, Start, and Remove Without Losing Data

Containers are ephemeral by design. You stop them with docker stop (graceful, sends SIGTERM) or docker kill (immediate, sends SIGKILL). Always prefer stop — it gives your app time to clean up connections and flush data.

To remove a container: docker rm . But if it's running, you need docker rm -f (force). Be careful — force remove skips the graceful shutdown.

For bulk cleanup: docker container prune removes all stopped containers. Add -f to skip confirmation. I run this weekly in CI to prevent zombie containers from eating disk space.

Pro tip: Use docker run --rm for temporary containers. The container is automatically removed when it exits. Perfect for build jobs or one-off scripts.

lifecycle.shBASH
1
2
3
4
5
6
7
8
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Start a temporary container that prints a message and exits
docker run --rm alpine echo "Hello, ephemeral world!"

# The container is gone after exit. Verify:
docker ps -a | grep alpine  # Should return nothing
Output
Hello, ephemeral world!
# No output from grep — container was removed automatically
💡Senior Shortcut:
Alias docker rm $(docker ps -aq) to clean all containers. But add a confirmation prompt — I've seen people wipe production containers by accident.

Debugging Running Containers: Logs, Exec, and Inspect

When a container misbehaves, your first move is docker logs. It streams stdout and stderr. Add -f to follow (like tail -f). Add --tail 100 to see only the last 100 lines.

If you need to get inside a running container: docker exec -it sh. The -it flags give you an interactive terminal. Inside, you can run commands like ps aux, netstat, or curl to diagnose issues.

docker inspect returns detailed metadata about the container — IP address, mounts, environment variables, restart policy. Pipe it to jq to extract specific fields: docker inspect | jq '.[0].NetworkSettings.IPAddress'.

For resource usage: docker stats shows live CPU, memory, and network I/O for all running containers. Use it to spot memory leaks or CPU spikes.

debug-container.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Run a simple web server in background
docker run -d --name debug-test -p 9999:80 nginx:alpine

# Check logs
docker logs --tail 10 debug-test

# Enter the container
docker exec -it debug-test sh
# Inside: run 'curl localhost' to test the web server

# Inspect IP address
docker inspect debug-test | jq -r '.[0].NetworkSettings.IPAddress'

# Clean up
docker rm -f debug-test
Output
10.0.0.2
# (plus nginx access logs from docker logs)
⚠ The Classic Bug:

Networking: How Containers Talk to Each Other and the World

By default, containers run on a bridge network (docker0). They can talk to each other via IP, but not by hostname. For service discovery, create a user-defined bridge network: docker network create mynet. Containers on the same user-defined network can resolve each other by container name.

To expose a container to the host, use -p. To isolate a container from the network, use --network none. For sharing the host's network stack (e.g., for performance), use --network host — but this reduces isolation.

Common issue: container can't reach the internet. Check DNS: docker run alpine cat /etc/resolv.conf. If DNS is wrong, pass --dns 8.8.8.8 to the container.

Production pattern: put your web server and API on the same network, but keep the database on a separate network with only the API connected to it.

networking.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Create a network
docker network create app-net

# Run two containers on the same network
docker run -d --name api --network app-net alpine sleep 3600
docker run -d --name web --network app-net alpine sleep 3600

# From web, ping api by name
docker exec web ping -c 1 api

# Clean up
docker rm -f api web
docker network rm app-net
Output
PING api (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.123 ms
--- api ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
🔥Production Trap:
Using --link (deprecated) instead of user-defined networks. --link only works on the default bridge and doesn't support DNS-based discovery. Always create a custom network.

Volumes and Data Persistence: Don't Lose Your Database

Containers are stateless by design. When you remove a container, all data inside it is gone. Volumes are the mechanism to persist data outside the container's writable layer.

There are two types: bind mounts (map a host directory) and named volumes (managed by Docker). Bind mounts are great for development — you can edit files on the host and see changes in the container. Named volumes are better for production — they're portable and can be backed up easily.

To create a named volume: docker volume create mydata. Then mount it: docker run -v mydata:/data .... To inspect: docker volume inspect mydata.

Never store database data in a bind mount to a host directory unless you're okay with permission issues. MySQL and Postgres containers run as non-root users; the host directory must have correct ownership. Named volumes handle this automatically.

volume-example.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Create a named volume
docker volume create pgdata

# Run Postgres with the volume
docker run -d \
  --name postgres \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

# Verify data persists
docker exec postgres psql -U postgres -c "CREATE DATABASE test;"
docker rm -f postgres

# Run a new Postgres container with the same volume
docker run -d \
  --name postgres2 \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

# Check if database 'test' exists
docker exec postgres2 psql -U postgres -l | grep test

# Clean up
docker rm -f postgres2
docker volume rm pgdata
Output
test | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
⚠ Never Do This:
Using --volumes-from to share volumes between containers. It's deprecated and creates tight coupling. Use named volumes and mount them independently.

Docker Compose: Orchestrating Multi-Container Apps Without the Pain

When your app has multiple services (web, API, database, cache), running them individually with docker run becomes a nightmare. Docker Compose lets you define all services in a YAML file and manage them with a single command.

docker-compose up -d starts everything. docker-compose down stops and removes containers, networks, and volumes (if configured). docker-compose logs -f tails logs from all services.

Compose creates a default network for all services, so they can resolve each other by service name. No need to manually create networks.

Production tip: Use Compose for development and staging, but for production, consider Docker Swarm or Kubernetes for better orchestration (rolling updates, scaling, health checks).

docker-compose.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# io.thecodeforge — DevOps tutorial

version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    depends_on:
      - api

  api:
    build: ./api
    environment:
      - DB_HOST=db
      - DB_PASSWORD=secret
    depends_on:
      - db

  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:
Output
Creating network "myapp_default" with driver "bridge"
Creating volume "myapp_pgdata" with default driver
Creating myapp_db_1 ... done
Creating myapp_api_1 ... done
Creating myapp_web_1 ... done
💡Senior Shortcut:
Use docker-compose config to validate your YAML before running. Catches indentation errors and missing fields.

Cleaning Up: Prune Like a Janitor, Not a Hoarder

Over time, Docker accumulates unused images, containers, volumes, and build cache. Disk space evaporates. The docker system prune command is your cleanup crew.

docker system prune removes stopped containers, dangling images, and unused networks. Add -a to remove all unused images (not just dangling). Add --volumes to also remove unused volumes. Be careful — --volumes will delete data volumes that aren't attached to a container.

For targeted cleanup: docker image prune -a removes all images not used by any container. docker container prune removes stopped containers. docker volume prune removes unused volumes.

I run docker system prune -a --volumes -f weekly in CI. But never on a production host without verifying no important volumes are dangling.

cleanup.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# See what's taking space
docker system df

# Dry run: show what would be removed
docker system prune -a --volumes --dry-run

# Actual cleanup (use with caution!)
docker system prune -a --volumes -f

# Verify freed space
docker system df
Output
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 12 5 2.345GB 1.123GB (47%)
Containers 8 3 1.234GB 0.567GB (45%)
Local Volumes 6 2 0.789GB 0.456GB (57%)
Build Cache 0 0 0B 0B
Total reclaimed space: 2.146GB
⚠ Production Trap:
Running docker system prune -a --volumes on a shared host without checking if any stopped containers have important volumes. Always inspect volumes first: docker volume ls and docker volume inspect <name>.

docker scout Commands: Supply Chain Security Built Into the CLI

Docker Scout is Docker's supply chain analysis tool, deeply integrated into the CLI. It analyzes images for CVEs, policy violations, and provenance. Docker has invested heavily here — it's now the default vulnerability scanner. Key commands: docker scout quickview shows a summary of vulnerabilities, CVSS scores, and available base image updates. docker scout compare highlights which packages changed. docker scout cves lists all CVEs with fix versions. docker scout policy evaluates the image against organization policies (e.g., 'no critical CVEs', 'must use signed base image'). docker scout recommendations suggests base image upgrades to reduce vulnerability count. For CI/CD, run docker scout policy --exit-code --policy-file policies.yaml to fail builds that violate policy. Scout uses the same vulnerability database as Docker Hub's analysis but runs locally without uploading your image — critical for air-gapped environments. Enable Scout: docker scout configure or set DOCKER_SCOUT_ENABLED=1.

docker-scout-examples.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Quick vulnerability overview
docker scout quickview nginx:1.25
# Output: (summary table with critical/high/medium/low counts)

# List CVEs with fix versions
docker scout cves --severity critical nginx:1.25

# Compare two images
docker scout compare nginx:1.25 nginx:1.26

# Policy evaluation (exit 1 if policy fails)
docker scout policy --exit-code --policy-file my-policy.yaml myapp:1.0

# Get base image upgrade recommendations
docker scout recommendations myapp:1.0

# Cache results locally for offline use
docker scout cache --directory /data/scout-cache
Output
# docker scout quickview output:
# Target │ Vulnerabilities
# │ C H M L U/C
# nginx:1.25 │ 0 2 5 12 2
💡Senior Shortcut: Scout in CI/CD Pipeline
Add docker scout policy --exit-code --policy-file docker-scout-policy.yaml $IMAGE to your CI pipeline. If policy fails, the build fails — no critical CVE reaches production. Pair with docker scout compare in PR comments to show vulnerability delta.

docker buildx Deep Dive: Multi-Platform Builds, QEMU, Bake, and Imagetools

docker buildx is the next-generation build system, replacing the classic docker build. Key capabilities: multi-platform builds (build once for linux/amd64, linux/arm64, linux/arm/v7), QEMU emulation for cross-compilation, BuildKit caching, and concurrent builds. Setup: docker buildx create --name mybuilder --bootstrap creates a builder instance. docker buildx use mybuilder activates it. To build for multiple platforms: docker buildx build --platform linux/amd64,linux/arm64 -t myapp:1.0 --push .. QEMU is used automatically for platforms that don't match the host — install it with docker run --privileged tonistiigi/binfmt --install all. New subcommands: buildx bake runs builds from a HCL/JSON/YAML bake file (like Compose for builds), supporting parallel builds and shared variables. buildx imagetools inspects and manipulates multi-arch image manifests without pulling layers: docker buildx imagetools inspect myapp:1.0 shows all platform manifests. buildx create --driver docker-container creates a remote builder running in a container — ideal for CI runners with limited Docker capabilities.

buildx-deep-dive.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Create and use a multi-platform builder
docker buildx create --name multiarch --driver docker-container --bootstrap
docker buildx use multiarch

# Install QEMU binfmt registrations (once per host)
docker run --privileged tonistiigi/binfmt --install all

# Build for amd64 + arm64 and push to registry
docker buildx build --platform linux/amd64,linux/arm64 \
  -t registry.example.com/myapp:1.0 --push .

# Inspect multi-arch manifest
docker buildx imagetools inspect registry.example.com/myapp:1.0

# Bake file example (docker-bake.hcl):
# target "default" {
#   platforms = ["linux/amd64", "linux/arm64"]
#   tags      = ["myapp:latest", "myapp:${BAKE_VERSION}"]
# }
docker buildx bake --set "BAKE_VERSION=1.0"
Output
Building platforms: linux/amd64, linux/arm64
Pushing: registry.example.com/myapp:1.0
Manifest list: registry.example.com/myapp:1.0 (2 manifests)
🔥Production Trap: QEMU Performance on ARM64 Emulation
QEMU emulation for arm64 on amd64 hosts is 5-10x slower than native. For CI, use native arm64 runners (Graviton, Apple Silicon) or cross-compile. Only use QEMU for final validation and manifest creation.

docker compose v2 Specific Commands: What Changed and What's New

Docker Compose v2 (docker compose without the hyphen, built into the CLI) supersedes v1 (docker-compose). Key differences: v2 is a Go plugin integrated into docker, not a standalone Python script. Performance is significantly better. New commands in v2: docker compose watch — monitors source files and auto-syncs changes to running containers (like docker-compose up --watch for development live reload). docker compose up -d in v2 has stricter dependency resolution (v1 sometimes started services out of order). docker compose config --dry-run validates the compose file and prints the resolved config without starting services — critical for CI/CD. docker compose create creates containers without starting them (useful for pre-provisioning). docker compose up --no-build --no-start --wait gives granular control over the lifecycle. Other v2 improvements: docker compose run inherits the compose file's network and volume config automatically (v1 required explicit flags). docker compose logs --since=5m --tail=100 supports native time-based filtering. Migration: docker compose commands accept all v1 flags but the YAML version field is ignored — v2 uses the Compose Specification directly.

compose-v2-commands.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Watch mode: auto-sync file changes to running containers
docker compose watch

# Dry-run: validate and print resolved config
docker compose config --dry-run

# Create containers without starting
docker compose create

# Start with explicit lifecycle flags
docker compose up --no-build --no-start --wait

# Logs with time-based filtering
docker compose logs --since=5m --tail=100 --follow

# Check v1 vs v2 version
docker compose version
# Output: Docker Compose version v2.29.1

# Old v1 check
docker-compose --version
# Output: docker-compose version 1.29.2 (may not be installed)
Output
# docker compose config --dry-run output:
services:
web:
image: nginx:alpine
ports:
- mode: ingress
target: 80
published: "8080"
protocol: tcp
⚠ Never Do This: Mixing v1 and v2 on the Same Project
The Compose Specification v2 files are compatible with both, but depends_on with conditions (condition: service_healthy) only works reliably in v2. Also, docker-compose down -v in v1 stops and removes volumes — in v2, use docker compose down --volumes (flag name changed).

New Docker CLI Commands You Should Know: init, sbom, manifest, trust, context, system df

Docker has several modern CLI commands beyond the basics. docker init scaffolds Docker assets for your project interactively — it detects the language (Go, Python, Node.js, Rust, Java, .NET) and generates optimized Dockerfile, .dockerignore, and compose.yml. Run docker init in your project root and answer the prompts. docker sbom generates a Software Bill of Materials (SPDX or CycloneDX format) for any image: docker sbom nginx:1.25 lists all packages, versions, and licenses. docker manifest inspects and pushes multi-arch manifests (prefer buildx imagetools now, but manifest is still useful for low-level ops). docker trust manages Docker Content Trust (DCT) signatures — docker trust sign myapp:1.0 && docker trust inspect --pretty myapp:1.0. docker context switches between Docker endpoints (local, remote SSH, Docker Swarm, ECS, ACI): docker context create remote --docker host=ssh://user@host && docker context use remote. docker system df shows disk usage by type (images, containers, volumes, build cache) — critical for capacity management. Combine with docker system df --verbose for per-image/per-volume breakdown.

new-cli-commands.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
# io.thecodeforge — DevOps tutorial

# Scaffold a new project
docker init
# (interactive prompts for language, port, etc.)

# Generate SBOM (Software Bill of Materials)
docker sbom nginx:1.25
# Output: (SPDX JSON with all packages and versions)

# Inspect and sign manifests
docker manifest inspect nginx:1.25
docker trust sign --local myapp:1.0

# Manage Docker contexts
docker context create prod --docker host=ssh://deploy@prod-host
docker context use prod
docker ps  # runs on remote host
docker context use default

# Detailed disk usage
docker system df --verbose

# Inspect trust data
docker trust inspect --pretty myapp:1.0
Output
# docker system df --verbose output:
Images space usage:
REPOSITORY TAG IMAGE ID CREATED SIZE SHARED SIZE
nginx 1.25 abc123 2 days ago 187MB 0B
# Context switched to 'prod'
# Running docker ps on remote host...
💡Senior Shortcut: Context Switching for Production Debugging
Define contexts for staging, prod, and dev: docker context create staging --docker host=tcp://staging-host:2375. Then docker context use staging and all commands run remotely. Never SSH into production hosts — use Docker contexts with TLS client certificates for secure remote access.
● Production incidentPOST-MORTEMseverity: high

The 4GB Container That Kept Dying

Symptom
A Node.js API container would run for 3-4 hours, then crash with FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory.
Assumption
The team assumed a memory leak in the application code.
Root cause
The container was started with --memory=4g but no --memory-swap limit. Docker defaulted swap to 4g, allowing the container to use up to 8g of combined memory+swap. The Node process grew beyond the 4g limit, hit swap, then the GC couldn't keep up, causing the crash.
Fix
Add --memory-swap=4g to match the memory limit, effectively disabling swap. Also add --memory-reservation=3g to signal pressure earlier.
Key lesson
  • Always set --memory-swap equal to --memory unless you explicitly want swap.
  • Otherwise, you're giving your container a memory limit that's effectively doubled.
Production debug guideSystematic recovery paths for the failure modes engineers actually hit.3 entries
Symptom · 01
Container exits with code 137 (OOMKilled)
Fix
1. Check docker inspect <container> | jq '.[0].State.OOMKilled' — if true, the container ran out of memory. 2. Increase memory limit: docker update --memory=512m <container>. 3. For long-term fix, add --memory-reservation to signal pressure earlier.
Symptom · 02
docker pull fails with net/http: TLS handshake timeout
Fix
1. Check internet connectivity: docker run alpine ping -c 4 google.com. 2. If DNS fails, add --dns 8.8.8.8 to Docker daemon config. 3. If behind a proxy, configure HTTP_PROXY in /etc/systemd/system/docker.service.d/http-proxy.conf.
Symptom · 03
docker build fails with no space left on device
Fix
1. Run docker system df to see disk usage. 2. Run docker system prune -a to free space. 3. If still full, check /var/lib/docker size and move it to a larger partition if needed.
★ Docker CLI Triage Cheat SheetFirst-response commands for when things go wrong — copy-paste ready.
Container won't start: `docker: Error response from daemon: driver failed programming external connectivity on endpoint`
Immediate action
Check if the port is already in use on the host.
Commands
sudo lsof -i :<port>
docker ps --filter "publish=<port>"
Fix now
Stop the container using the port: docker stop <container> or change the host port mapping.
`docker exec` fails: `the input device is not a TTY`+
Immediate action
You forgot the `-it` flags.
Commands
docker exec -it <container> sh
Fix now
Always use -it together for interactive sessions.
Container runs but can't connect to the internet+
Immediate action
Check DNS resolution inside the container.
Commands
docker exec <container> cat /etc/resolv.conf
docker exec <container> ping -c 4 8.8.8.8
Fix now
Pass --dns 8.8.8.8 to docker run or configure Docker daemon DNS.
`docker build` is slow because it's not using cache+
Immediate action
Check if the Dockerfile layers are ordered correctly.
Commands
docker build --no-cache -t test .
docker history test
Fix now
Reorder Dockerfile: put infrequent changes (like COPY package*.json) before frequent changes (like COPY .).
Featuredocker rundocker-compose up
Single containerYesYes (but overkill)
Multi-containerManual (multiple commands)Yes (single command)
Network creationManual (--network)Automatic
Volume managementManual (-v)Declarative in YAML
Environment variablesManual (-e)Declarative in YAML
ScalingNot supportedSupported (docker-compose up --scale)
Production readinessLow (no health checks, rolling updates)Medium (basic health checks)
⚙ Quick Reference
13 commands from this guide
FileCommand / CodePurpose
check-docker-status.shsystemctl status dockerWhy You Need the Docker CLI
DockerfileFROM node:18-alpine AS builderImage Management
run-nginx.shdocker run -d \Running Containers
lifecycle.shdocker run --rm alpine echo "Hello, ephemeral world!"Container Lifecycle
debug-container.shdocker run -d --name debug-test -p 9999:80 nginx:alpineDebugging Running Containers
networking.shdocker network create app-netNetworking
volume-example.shdocker volume create pgdataVolumes and Data Persistence
docker-compose.ymlversion: '3.8'Docker Compose
cleanup.shdocker system dfCleaning Up
docker-scout-examples.shdocker scout quickview nginx:1.25docker scout Commands
buildx-deep-dive.shdocker buildx create --name multiarch --driver docker-container --bootstrapdocker buildx Deep Dive
compose-v2-commands.shdocker compose watchdocker compose v2 Specific Commands
new-cli-commands.shdocker initNew Docker CLI Commands You Should Know

Key takeaways

1
Always pin image tags to specific versions
never use latest in production.
2
Set --restart unless-stopped for long-running services to survive crashes.
3
Use named volumes for production data
bind mounts are for development only.
4
docker system prune -a --volumes is powerful but dangerous
always dry-run first.
5
Docker Scout is mandatory for any production pipeline
policy-as-code supply chain security stops critical CVEs before they reach your registry.
6
Buildx multi-arch builds are table stakes for modern infrastructure
arm64 adoption is accelerating, and single-arch images will increasingly be a deployment blocker.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
What happens when you run `docker run` without `--restart` and the proce...
Q02SENIOR
When would you choose a bind mount over a named volume in production?
Q03SENIOR
A container is running out of memory and getting OOMKilled. What's your ...
Q04JUNIOR
What is the difference between `docker stop` and `docker kill`?
Q05SENIOR
You have a multi-service application. How would you design the networkin...
Q06SENIOR
How would you handle rolling updates of a containerized application with...
Q07SENIOR
How would you implement a secure multi-arch Docker build pipeline that e...
Q08SENIOR
You're debugging a production issue where `docker compose up -d` on v2 s...
Q01 of 08SENIOR

What happens when you run `docker run` without `--restart` and the process inside crashes? How would you handle this in production?

ANSWER
The container exits and stays stopped. In production, always use --restart unless-stopped to automatically restart on crash. For critical services, add health checks and monitoring to alert if the container restarts too frequently.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
What is the difference between `docker run` and `docker start`?
02
How do I copy a file from a container to my host?
03
How do I see the logs of a container that has already exited?
04
What happens to the data in a container when I run `docker rm`?
05
What is Docker Scout and how do I use it to check for vulnerabilities?
06
How do I build multi-architecture Docker images for both amd64 and arm64?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
Verified
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
🔥

That's Docker. Mark it forged?

7 min read · try the examples if you haven't

Previous
Docker Installation and Setup
20 / 43 · Docker
Next
Dockerfile Best Practices