Docker (2025): ship faster with smaller, safer images
Learn modern docker workflows: lean Dockerfiles, multi-stage builds, Compose for dev, secure registries, SBOM and vulnerability scans, and CI/CD integration.
Image Size
Multi-stage & minimal base
Build Cache
Layer strategy
Security
Non-root & scans
What is Docker?
Docker runs applications in isolated containers built from images. Images bundle the runtime, OS libraries, and your app so it behaves consistently on laptops, CI, and servers.
Install & CLI basics
Docker Desktop / Colima
Local engine
# Homebrew brew install --cask docker # or: brew install colima docker docker-compose
Enable BuildKit: export DOCKER_BUILDKIT=1.
Write a production Dockerfile
# Example: Node.js app (multi-stage, non-root, distroless) # ---- build stage ---- FROM node:20-alpine AS build WORKDIR /app COPY package*.json ./ RUN npm ci --omit=dev=false COPY . . RUN npm run build && npm prune --omit=dev # ---- runtime stage ---- FROM gcr.io/distroless/nodejs20 WORKDIR /app USER 10001 COPY --from=build /app/dist ./dist COPY --from=build /app/node_modules ./node_modules ENV NODE_ENV=production EXPOSE 3000 CMD ["dist/server.js"]
.dockerignore to exclude node_modules, tests, and artifacts you rebuild in the image.Build, tag & push images
Build & run
Local loop
docker build -t app:dev . docker run --rm -p 3000:3000 --env-file .env app:dev
Tag & push
Registry
docker tag app:dev ghcr.io/org/app:2025-09-23 docker push ghcr.io/org/app:2025-09-23
Docker Compose for development
services:
web:
build: .
ports: ["3000:3000"]
env_file: .env
depends_on: [db]
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: example
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata: {}Start with docker compose up --build; stop via down -v to remove volumes.
Networking (ports, bridges)
- Bridge network is default; containers talk via service name.
- Publish ports with
-p host:container. - Use user-defined networks to isolate stacks.
Volumes & persistence
- Named volumes survive container restarts; bind mounts mirror host dirs.
- Back up volumes with
docker run --rm -v vol:/data busybox tar. - Set proper UID/GID; avoid root-owned data for dev convenience.
Multi-stage builds & cache
Layering strategy
Cache hits
BuildKit
Advanced cache
docker buildx build --cache-from=type=registry,ref=ghcr.io/org/app:cache --cache-to=type=registry,ref=ghcr.io/org/app:cache,mode=max -t ghcr.io/org/app:latest .
CI/CD pipelines
GitHub Actions
Build & push
name: docker-build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with: { registry: ghcr.io, username: ${{ github.actor }}, password: ${{ secrets.GITHUB_TOKEN }} }
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/org/app:latestSupply chain
Sign & SBOM
cosign sign ghcr.io/org/app:latest syft packages ghcr.io/org/app:latest -o cyclonedx-json > sbom.json
Security & runtime hardening
- Run as non-root; drop capabilities; read-only root filesystem when possible.
- Keep base images updated; rebuild on CVE notifications.
- Never bake secrets into the image; mount at runtime.
Slim images checklist
- Use multi-stage; copy only runtime artifacts
- Select minimal base (alpine/distroless/ubi-micro)
- Clean caches & package managers in the same layer
- Compress images at the registry; enable zstd layers if supported
- Use
.dockerignore; pin versions for deterministic builds
Docs & dashboards: image privacy tip
Container guides often include screenshots and diagrams. Those images can carry hidden EXIF/GPS or authoring traces. Before pushing them to public repos or wikis, sanitize the metadata.
FAQ
Ship smaller, safer Docker images — protect privacy
Optimize Dockerfiles, automate builds, sign and scan images. For visuals you publish alongside, scrub hidden EXIF/GPS.