Image Commands
Docker images are immutable templates that contain everything needed to run an application: the OS layer, runtime, libraries, code, and configuration. Every container starts from an image. Mastering image commands is the foundation of working with Docker.
Building Images
# Build from Dockerfile in current directory
docker build -t myapp:latest .
# Build with a specific Dockerfile
docker build -f Dockerfile.prod -t myapp:prod .
# Build with build arguments
docker build --build-arg NODE_ENV=production -t myapp:prod .
# Build without cache (force full rebuild)
docker build --no-cache -t myapp:latest .
# Build for a specific platform
docker build --platform linux/amd64 -t myapp:amd64 .
# Build for multiple platforms with buildx
docker buildx build --platform linux/amd64,linux/arm64 \
-t myregistry.com/myapp:latest --push .
Managing Images
# List all local images
docker images
# List with formatted output
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
# Pull an image from a registry
docker pull node:22-alpine
# Pull a specific digest (immutable)
docker pull node@sha256:abc123...
# Push to a registry
docker tag myapp:latest myregistry.com/myapp:v1.2.3
docker push myregistry.com/myapp:v1.2.3
# Inspect image metadata (layers, env, ports)
docker inspect node:22-alpine
# View image layer history
docker history myapp:latest --no-trunc
# Save image to tar archive
docker save myapp:latest -o myapp.tar
# Load image from tar archive
docker load -i myapp.tar
# Remove an image
docker rmi myapp:latest
# Remove all dangling images (untagged)
docker image prune
# Remove ALL unused images
docker image prune -a
Use docker images --filter "dangling=true" to see images that are no longer tagged. These accumulate fast during development and consume significant disk space.
Container Lifecycle
Containers are running instances of images. They have their own filesystem, network, and process space. Understanding the container lifecycle — create, start, stop, restart, and remove — is essential for day-to-day Docker work.
Running Containers
# Run a container (pull image if needed)
docker run nginx:alpine
# Run in detached mode (background)
docker run -d --name webserver nginx:alpine
# Run with port mapping (host:container)
docker run -d -p 8080:80 nginx:alpine
# Run with environment variables
docker run -d -e NODE_ENV=production -e PORT=3000 myapp
# Run with env file
docker run -d --env-file .env myapp
# Run with volume mount (host path)
docker run -d -v $(pwd)/data:/app/data myapp
# Run with named volume
docker run -d -v app-data:/app/data myapp
# Run interactively with a shell
docker run -it ubuntu:22.04 /bin/bash
# Run with automatic removal on exit
docker run --rm -it node:22-alpine node -e "console.log('hello')"
# Run with memory and CPU limits
docker run -d --memory=512m --cpus=1.5 myapp
# Run with a restart policy
docker run -d --restart=unless-stopped myapp
# Run on a specific network
docker run -d --network=mynetwork myapp
For managing environment variables across your containers, the .env File Editor helps you create and validate .env files with syntax highlighting and duplicate key detection.
Managing Running Containers
# List running containers
docker ps
# List ALL containers (including stopped)
docker ps -a
# List with custom format
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
# Stop a container (SIGTERM, then SIGKILL after 10s)
docker stop webserver
# Stop with custom timeout
docker stop -t 30 webserver
# Start a stopped container
docker start webserver
# Restart a container
docker restart webserver
# Pause / unpause container processes
docker pause webserver
docker unpause webserver
# Remove a stopped container
docker rm webserver
# Force remove a running container
docker rm -f webserver
# Remove all stopped containers
docker container prune
# Execute a command in a running container
docker exec -it webserver /bin/sh
# Copy files between host and container
docker cp webserver:/etc/nginx/nginx.conf ./nginx.conf
docker cp ./app.conf webserver:/etc/nginx/conf.d/
# View resource usage (live)
docker stats
# View resource usage for specific containers
docker stats webserver api-server
Volumes and Data Persistence
Container filesystems are ephemeral. When a container is removed, its data disappears. Volumes solve this by storing data outside the container lifecycle. There are three types: named volumes (managed by Docker), bind mounts (host directory mapping), and tmpfs mounts (in-memory only).
# Create a named volume
docker volume create pgdata
# List all volumes
docker volume ls
# Inspect volume details (mount point, driver)
docker volume inspect pgdata
# Run container with named volume
docker run -d -v pgdata:/var/lib/postgresql/data postgres:16
# Run with bind mount (host directory)
docker run -d -v $(pwd)/src:/app/src myapp
# Run with read-only bind mount
docker run -d -v $(pwd)/config:/app/config:ro myapp
# Run with tmpfs mount (in-memory, never written to disk)
docker run -d --tmpfs /tmp:rw,size=64m myapp
# Backup a volume to tar archive
docker run --rm -v pgdata:/data -v $(pwd):/backup \
alpine tar czf /backup/pgdata-backup.tar.gz -C /data .
# Restore a volume from tar archive
docker run --rm -v pgdata:/data -v $(pwd):/backup \
alpine tar xzf /backup/pgdata-backup.tar.gz -C /data
# Remove a specific volume
docker volume rm pgdata
# Remove all unused volumes
docker volume prune
docker volume prune permanently deletes data. Always verify which volumes are unused with docker volume ls --filter "dangling=true" before pruning in production.
Network Commands
Docker networking controls how containers communicate with each other and the outside world. The default bridge network allows basic connectivity, but custom networks enable DNS-based service discovery where containers reach each other by name instead of IP address.
# List all networks
docker network ls
# Create a custom bridge network
docker network create mynetwork
# Create with specific subnet and gateway
docker network create --subnet=172.20.0.0/16 --gateway=172.20.0.1 mynetwork
# Inspect network details (connected containers, config)
docker network inspect mynetwork
# Run container on a custom network
docker run -d --network=mynetwork --name api myapp
# Connect a running container to a network
docker network connect mynetwork webserver
# Disconnect a container from a network
docker network disconnect mynetwork webserver
# Remove a network
docker network rm mynetwork
# Remove all unused networks
docker network prune
Network Types
| Driver | Use Case | DNS Discovery |
|---|---|---|
bridge |
Default for standalone containers | Custom bridges only |
host |
Container shares host network stack | N/A |
none |
Complete network isolation | N/A |
overlay |
Multi-host (Swarm / multi-node) | Yes |
macvlan |
Assign MAC address, appear as physical device | No |
For reverse-proxy setups in front of your Docker containers, the Nginx Config Generator produces production-ready configurations with SSL termination, caching, and upstream blocks.
Dockerfile Instructions
A Dockerfile is a text file that defines how to build an image. Each instruction creates a layer. Understanding the instructions and their ordering is critical for building small, fast, cacheable images.
Core Instructions Reference
| Instruction | Purpose | Example |
|---|---|---|
FROM |
Base image | FROM node:22-alpine |
WORKDIR |
Set working directory | WORKDIR /app |
COPY |
Copy files from build context | COPY package*.json ./ |
RUN |
Execute command during build | RUN npm ci --production |
ENV |
Set environment variable | ENV NODE_ENV=production |
ARG |
Build-time variable | ARG VERSION=1.0 |
EXPOSE |
Document listening port | EXPOSE 3000 |
CMD |
Default container command | CMD ["node", "server.js"] |
ENTRYPOINT |
Fixed container executable | ENTRYPOINT ["node"] |
USER |
Switch to non-root user | USER appuser |
HEALTHCHECK |
Container health monitoring | HEALTHCHECK CMD curl -f http://localhost/ |
Optimized Dockerfile Example
FROM node:22-alpine AS base
WORKDIR /app
# Install dependencies first (cached unless package files change)
COPY package.json package-lock.json ./
RUN npm ci --production && npm cache clean --force
# Copy application code
COPY src/ ./src/
COPY public/ ./public/
# Create non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
# Document port and set default command
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -q --spider http://localhost:3000/health || exit 1
CMD ["node", "src/server.js"]
Order instructions from least to most frequently changed. COPY package*.json before COPY src/ ensures npm install only re-runs when dependencies change, not when you edit source code.
Multi-Stage Builds
Multi-stage builds use multiple FROM statements in a single Dockerfile. Each stage can use a different base image. You build your application in a stage with all the tools, then copy only the compiled output into a minimal runtime image. This is the single most effective technique for reducing Docker image size.
Node.js Multi-Stage Build
# Stage 1: Build
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Production runtime
FROM node:22-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --production && npm cache clean --force
COPY --from=builder /app/dist ./dist
RUN addgroup -S app && adduser -S app -G app
USER app
EXPOSE 3000
CMD ["node", "dist/server.js"]
Go Multi-Stage Build (Scratch Image)
# Stage 1: Build with full Go toolchain
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server ./cmd/server
# Stage 2: Minimal runtime (under 10MB)
FROM scratch
COPY --from=builder /server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
A Go binary compiled with CGO_ENABLED=0 can run in a scratch image — literally an empty filesystem. The final image contains only your binary and CA certificates, often under 10MB.
Docker Compose
Docker Compose defines multi-container applications in a single YAML file. Instead of running multiple docker run commands with long argument lists, you declare everything in compose.yaml (or docker-compose.yml) and manage it with one command. Compose automatically creates a custom network for each project, enabling service-to-service DNS resolution.
Full-Stack Compose Example
services:
web:
build:
context: ./frontend
dockerfile: Dockerfile.prod
ports:
- "3000:3000"
environment:
- API_URL=http://api:8080
depends_on:
api:
condition: service_healthy
restart: unless-stopped
api:
build: ./backend
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://app:secret@db:5432/myapp
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
interval: 10s
timeout: 5s
retries: 3
restart: unless-stopped
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d myapp"]
interval: 5s
timeout: 3s
retries: 5
restart: unless-stopped
cache:
image: redis:7-alpine
volumes:
- redis-data:/data
restart: unless-stopped
volumes:
pgdata:
redis-data:
Need to build compose files quickly? The Docker Compose Generator lets you visually select services, configure ports, volumes, and networks, then export a production-ready YAML file.
Compose Commands
# Start all services (build if needed)
docker compose up -d
# Start with forced rebuild
docker compose up -d --build
# Stop all services
docker compose down
# Stop and remove volumes (destroys data)
docker compose down -v
# View logs for all services
docker compose logs -f
# View logs for a specific service
docker compose logs -f api
# List running services
docker compose ps
# Scale a service
docker compose up -d --scale api=3
# Execute command in a running service
docker compose exec api /bin/sh
# Run a one-off command
docker compose run --rm api npm run migrate
# Pull latest images for all services
docker compose pull
# Validate compose file syntax
docker compose config
When editing YAML configuration files, the YAML Editor catches indentation errors and invalid syntax before Docker does.
Logs and Debugging
When a container misbehaves — crashes on startup, returns errors, or runs out of resources — these commands help you find the problem.
# View container logs
docker logs webserver
# Follow logs in real time
docker logs -f webserver
# Show last 100 lines
docker logs --tail 100 webserver
# Show logs with timestamps
docker logs -t webserver
# Show logs since a specific time
docker logs --since 2026-02-14T10:00:00 webserver
docker logs --since 30m webserver
# Open a shell inside a running container
docker exec -it webserver /bin/sh
# Inspect full container configuration
docker inspect webserver
# Extract specific field with Go template
docker inspect --format='{{.State.Status}}' webserver
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' webserver
# View filesystem changes since container started
docker diff webserver
# View live resource usage
docker stats webserver
# View all running processes in a container
docker top webserver
# View port mappings
docker port webserver
# View system-wide events (live)
docker events
docker events --filter type=container --filter event=die
# Check container health status
docker inspect --format='{{.State.Health.Status}}' webserver
When inspecting the JSON output from docker inspect, the JSON Formatter makes deeply nested container configurations readable with syntax highlighting and collapsible tree views.
If a container exits immediately, override the entrypoint: docker run -it --entrypoint /bin/sh myapp. This gives you a shell inside the container without running the application, so you can inspect the environment, check file permissions, and test commands manually.
Cleanup and Disk Management
Docker accumulates images, containers, volumes, and build cache fast. Without regular cleanup, disk usage can grow by several GB per week during active development. These commands free space and keep your system healthy.
# View total disk usage by Docker
docker system df
# View detailed breakdown
docker system df -v
# Remove ALL unused data (stopped containers, unused networks,
# dangling images, build cache)
docker system prune
# Also remove unused images (not just dangling)
docker system prune -a
# Also remove unused volumes (CAUTION: deletes data)
docker system prune -a --volumes
# Targeted cleanup
docker container prune # stopped containers
docker image prune # dangling images
docker image prune -a # all unused images
docker volume prune # unused volumes
docker network prune # unused networks
docker builder prune # build cache
# Remove images older than 24 hours
docker image prune -a --filter "until=24h"
# Remove containers stopped more than 1 hour ago
docker container prune --filter "until=1h"
# Kill all running containers (emergency)
docker kill $(docker ps -q)
# Remove all containers (running and stopped)
docker rm -f $(docker ps -aq)
# Remove all images
docker rmi -f $(docker images -q)
docker system prune -a --volumes is destructive. It removes all unused volumes including database data. In production, always use targeted prune commands and verify with --dry-run or docker system df -v first.
Security Best Practices
Container security is not optional. A misconfigured container can expose your host system, leak credentials, or become an entry point for attackers. These practices cover the critical areas.
Non-Root User
# Install dependencies as root
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
# Create and switch to non-root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
RUN chown -R appuser:appgroup /app
USER appuser
COPY --chown=appuser:appgroup . .
CMD ["node", "server.js"]
Runtime Security Flags
# Read-only filesystem
docker run --read-only --tmpfs /tmp myapp
# Drop all capabilities, add only what you need
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp
# Prevent privilege escalation
docker run --security-opt=no-new-privileges myapp
# Limit memory and CPU
docker run --memory=256m --cpus=0.5 myapp
# Disable inter-container communication on default bridge
docker network create --opt com.docker.network.bridge.enable_icc=false isolated
# Run with a specific user (override Dockerfile USER)
docker run --user 1000:1000 myapp
# Mount secrets as read-only files (not env vars)
docker run -v ./secrets/db-password:/run/secrets/db-password:ro myapp
Image Scanning
# Scan with Docker Scout (built into Docker Desktop)
docker scout cves myapp:latest
# Scan with Trivy (open source)
trivy image myapp:latest
# Scan during CI (fail on critical/high)
trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:latest
1) Use minimal base images (alpine/distroless). 2) Run as non-root. 3) Drop all capabilities. 4) Use read-only filesystems. 5) Never put secrets in ENV or ARG. 6) Scan images in CI. 7) Pin image versions by digest. 8) Use .dockerignore.
Related Developer Tools
Frequently Asked Questions
docker run creates and starts a new container from an image. docker exec runs a command inside an already running container. Use docker run when you need a fresh container instance. Use docker exec -it <container> /bin/sh to open a shell inside a container that is already running, which is useful for debugging, inspecting logs, or running one-off commands without stopping the container.
Run docker system prune to remove all stopped containers, unused networks, dangling images, and the build cache in one command. Add the -a flag (docker system prune -a) to also remove images not referenced by any container, not just dangling ones. Add --volumes to include unused volumes. For more targeted cleanup, use docker container prune (stopped containers only), docker image prune -a (unused images), or docker volume prune (unused volumes). Always check what will be removed with the --dry-run flag first in production environments.
COPY simply copies files and directories from the build context into the image. ADD does the same but has two additional features: it can extract tar archives automatically (ADD app.tar.gz /app will unpack the archive), and it can download files from URLs. Docker best practice is to always use COPY unless you specifically need tar extraction. ADD from URLs is discouraged because it creates larger images and you cannot leverage layer caching. If you need to download a file, use RUN curl or RUN wget instead so you can also verify checksums and delete the archive in the same layer.
There are several approaches. Use the -e flag with docker run: docker run -e DATABASE_URL=postgres://... myapp. Pass multiple variables from a file with --env-file: docker run --env-file .env myapp. In Docker Compose, use the environment key to set variables directly, or env_file to reference an .env file. For build-time variables (used in Dockerfile instructions), use ARG in your Dockerfile and pass values with --build-arg during docker build. Important: never put secrets in ARG or ENV instructions since they are visible in image layers. For secrets, use Docker secrets in Swarm mode, mount a secrets file at runtime, or use a secrets manager.
A multi-stage build uses multiple FROM statements in a single Dockerfile. Each FROM starts a new build stage. You compile or build your application in an early stage that has all the build tools (compilers, node_modules, dev dependencies), then COPY only the final output into a minimal runtime image. This dramatically reduces image size because the final image does not contain build tools, source code, or intermediate files. For example, a Node.js app might use a node:22 image (over 1GB) to run npm install and npm run build, then copy the output into a node:22-alpine image (under 200MB). A Go application can be compiled in a golang image and copied into a scratch or distroless image that is under 10MB.
Docker provides three default networks: bridge (default for standalone containers), host (shares the host network stack), and none (no networking). When you run docker run without specifying a network, containers join the default bridge network where they can communicate by IP address but not by container name. Custom bridge networks enable DNS-based service discovery, meaning containers can reach each other by name (for example, a web container can connect to a database container using the hostname db). Docker Compose creates a custom network automatically for each project, which is why services in a compose file can reference each other by service name. Use custom networks to isolate groups of containers, control which services can communicate, and keep your architecture secure.