Docker Cheatsheet

Docker packages your application and its dependencies into a portable container image. These are the commands and patterns you will use on every cloud project.

Image Commands#

CommandWhat it does
docker pull nginx:1.25Download an image from a registry
docker build -t my-app:1.0 .Build an image from a Dockerfile in the current directory
docker push myrepo/my-app:1.0Push an image to a registry
docker tag my-app:1.0 myrepo/my-app:1.0Add a new tag to an existing image
docker imagesList local images
docker rmi my-app:1.0Delete a local image
docker inspect my-app:1.0Show low-level JSON metadata for an image
docker history my-app:1.0Show the layers that make up an image

Container Lifecycle Commands#

CommandWhat it does
docker run my-app:1.0Create and start a new container
docker start <name>Start a stopped container
docker stop <name>Gracefully stop a running container (SIGTERM → SIGKILL)
docker restart <name>Stop and start a container
docker rm <name>Delete a stopped container
docker psList running containers
docker ps -aList all containers including stopped ones
docker exec -it <name> /bin/shOpen an interactive shell in a running container
docker logs <name>Print container stdout/stderr
docker logs -f <name>Stream live logs
docker inspect <name>Show low-level JSON metadata for a container
docker statsLive CPU, memory, and network usage for all containers
docker cp <name>:/app/file.txt ./file.txtCopy a file out of a container

Key docker run Flags#

docker run \
  -d \                          # detach (run in background)
  -p 8080:80 \                  # map host port 8080 to container port 80
  -v /host/data:/app/data \     # bind mount a host directory
  --name my-container \         # give the container a name
  -e DB_HOST=localhost \        # set an environment variable
  --env-file .env \             # load env vars from a file
  --network my-network \        # attach to a named network
  --rm \                        # auto-delete container when it stops
  -it \                         # interactive + allocate a TTY
  my-app:1.0

Dockerfile Instructions#

# Base image — always use a specific version tag
FROM node:20-alpine

# Set the working directory inside the container
WORKDIR /app

# Copy dependency files first (improves layer caching)
COPY package*.json ./

# Run commands during the build
RUN npm ci --only=production

# Copy the rest of the source code
COPY . .

# Declare the port the app listens on (documentation only — doesn't publish it)
EXPOSE 3000

# Set environment variables available at runtime
ENV NODE_ENV=production

# The default command to run when the container starts
# Use ENTRYPOINT + CMD together for flexibility
ENTRYPOINT ["node"]
CMD ["server.js"]

CMD sets the default command but can be overridden at docker run. ENTRYPOINT sets the executable and is harder to override (requires --entrypoint flag). For most apps, use both: ENTRYPOINT ["node"] and CMD ["server.js"].

Multi-Stage Build#

Multi-stage builds produce small final images by keeping build tools out of the runtime image.

# Stage 1: build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# Stage 2: runtime image (much smaller)
FROM node:20-alpine AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

The final image only contains what was copied in the runtime stage. The build tools from node:20 are discarded.

Docker Compose#

Minimal docker-compose.yml with two services#

version: "3.9"

services:
  web:
    build: .
    ports:
      - "8080:3000"
    environment:
      - NODE_ENV=production
      - DB_HOST=db
    depends_on:
      - db
    volumes:
      - ./uploads:/app/uploads

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Docker Compose Commands#

docker compose up -d          # start all services in the background
docker compose down           # stop and remove containers and networks
docker compose down -v        # also delete named volumes
docker compose logs -f web    # stream logs from the web service
docker compose ps             # list running services
docker compose exec web sh    # open a shell in the running web container
docker compose build          # rebuild images without starting
docker compose pull           # pull the latest versions of images

Network Commands#

docker network ls                        # list all networks
docker network create my-network         # create a bridge network
docker network inspect my-network        # show containers and config for a network
docker network connect my-network <container>   # attach a running container to a network
docker network disconnect my-network <container>

Containers on the same named network can reach each other by container name. The default bridge network does not support DNS resolution by name.

Volume Commands#

docker volume ls                     # list named volumes
docker volume create my-volume       # create a named volume
docker volume inspect my-volume      # show mount point and metadata
docker volume prune                  # delete all unused volumes (irreversible)

Named volumes (managed by Docker) outlive containers. Bind mounts (a host path) are useful in development but create host-dependency for production.

System Cleanup#

docker system prune          # remove stopped containers, dangling images, unused networks
docker system prune -a       # also remove images not used by any container
docker image prune           # remove dangling images (untagged)
docker image prune -a        # remove all unused images
docker container prune       # remove all stopped containers
docker volume prune          # remove all unused volumes

Run docker system df first to see how much space Docker is using before pruning.

Common Mistakes#

Not using .dockerignore. Without it, COPY . . will include node_modules, .git, .env, and other large or sensitive directories. Create a .dockerignore at the same level as your Dockerfile.

Running as root. By default, containers run as root. Add a USER instruction to drop to a non-root user before the CMD:

RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser

Large layer count from repeated RUN instructions. Each RUN creates a new layer. Chain related commands with && and clean up in the same layer:

RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

Not tagging image versions. Using :latest in production makes rollbacks hard. Tag images with a version number or git commit SHA.

Copying source before installing dependencies. This busts the npm/pip cache on every source code change. Copy dependency files first, run install, then copy source.