Docker

Core Concepts

TermWhat it is
ImageImmutable read-only template (filesystem + metadata) built from a Dockerfile
ContainerA running (or stopped) instance of an image — isolated process with its own filesystem layer
RegistryWhere images are stored/shared (Docker Hub, GHCR, ECR)
LayerEach Dockerfile instruction adds a cached, reusable filesystem layer
VolumePersistent storage that outlives a container’s lifecycle

Containers share the host kernel (unlike VMs) — that’s why they start in milliseconds and use far less overhead.

Dockerfile Basics

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
ENV NODE_ENV=production
USER node
CMD ["node", "server.js"]
InstructionPurpose
FROMbase image
WORKDIRset cwd for subsequent instructions
COPY / ADDcopy files in (prefer COPY; ADD also unpacks archives/fetches URLs)
RUNexecute a command at build time (new layer)
ENVset environment variables
EXPOSEdocument the listening port (doesn’t publish it)
CMDdefault command when the container starts (overridable)
ENTRYPOINTfixed executable; CMD becomes its default args
USERdrop root for the running process

Build And Run

docker build -t myapp:latest .              # build image from Dockerfile in cwd
docker build -t myapp:latest -f Dockerfile.prod .
docker run myapp:latest                        # run, foreground
docker run -d --name web -p 8080:3000 myapp:latest  # detached, named, port-mapped
docker run -it myapp:latest sh                    # interactive shell in a new container
docker run --rm myapp:latest                        # auto-remove container on exit
docker run -e API_KEY=secret myapp:latest             # pass an env var

-p host:container maps ports; -e sets env vars; -d detaches; --rm cleans up automatically — useful for one-off/debug runs.

Image Management

docker images                    # list local images
docker pull nginx:latest           # download an image
docker push myrepo/myapp:1.0         # upload to a registry
docker tag myapp:latest myrepo/myapp:1.0
docker rmi myapp:latest                # remove an image
docker image prune                       # remove dangling (untagged) images
docker image prune -a                      # remove all unused images
docker history myapp:latest                  # inspect layer sizes

Container Lifecycle

docker ps                    # running containers
docker ps -a                   # all containers, including stopped
docker start <name>              # start a stopped container
docker stop <name>                 # graceful stop (SIGTERM, then SIGKILL after timeout)
docker restart <name>                # stop + start
docker rm <name>                       # remove a stopped container
docker rm -f <name>                      # force remove (even if running)
docker exec -it <name> sh                  # shell into a running container
docker logs -f <name>                        # follow logs

Volumes And Bind Mounts

docker volume create mydata
docker run -v mydata:/app/data myapp          # named volume (Docker-managed storage)
docker run -v $(pwd):/app myapp                 # bind mount (host path, great for local dev)
docker run --mount type=volume,src=mydata,dst=/app/data myapp  # explicit long-form syntax
docker volume ls
docker volume rm mydata

Named volumes persist and are portable across containers; bind mounts tie directly to a host path — ideal for live-reloading source code during development.

Networking

docker network ls
docker network create mynet
docker run --network mynet --name api myapp
docker run --network mynet --name web nginx   # `web` can reach `api` by container name (DNS)
docker network inspect mynet

By default, containers on the same user-defined network resolve each other by container/service name — no manual IP wiring needed.

Docker Compose

# docker-compose.yml
services:
  web:
    build: .
    ports:
      - "8080:3000"
    environment:
      - NODE_ENV=production
    depends_on:
      - db
  db:
    image: postgres:16
    volumes:
      - dbdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=secret
volumes:
  dbdata:
docker compose up -d          # start all services, detached
docker compose down             # stop and remove containers/network
docker compose logs -f web        # follow logs for one service
docker compose exec web sh          # shell into a running service
docker compose build --no-cache       # rebuild ignoring layer cache

Multi Stage Builds

# build stage — has full toolchain, produces artifacts
FROM node:20 AS builder
WORKDIR /app
COPY . .
RUN npm ci && npm run build

# runtime stage — only ships the built output, smaller final image
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm ci --omit=dev
CMD ["node", "dist/server.js"]

Multi-stage builds keep the final image lean — compilers, dev dependencies, and source maps used only during build never ship to production.

Debugging Containers

docker inspect <name>              # full JSON metadata (env, mounts, network, etc.)
docker logs --tail 100 <name>        # last 100 log lines
docker stats                           # live CPU/memory usage per container
docker exec -it <name> sh                # poke around inside a running container
docker events                              # stream real-time Docker daemon events
docker system df                             # disk usage summary (images, containers, volumes)
docker system prune -a --volumes               # reclaim disk space — destructive, review first