Batch 4: Cloud Ansible AWS & Azure Docker & Podman Apache & NGINX ← Full TOC

← Docker Index

📄 Dockerfile Best Practices — Multi-stage builds, layer caching, security, .dockerignore

A well-written Dockerfile produces small, secure, fast-building images. Multi-stage builds separate build dependencies from runtime. Layer order matters — put rarely-changing layers first to maximize cache reuse.

📋 Reference

ItemSyntaxDescription
FROMFROM ubuntu:22.04 / FROM python:3.12-slimBase image — use official slim/alpine variants
FROM multi-stageFROM golang:1.22 AS builder ... FROM scratchBuild in one stage, copy binary to minimal image
AS builderFROM node:20 AS builderName a build stage for reference
WORKDIRWORKDIR /appSet working directory — creates if not exists
COPYCOPY src/ /app/src/Copy files from build context to image
COPY --fromCOPY --from=builder /app/binary /usr/local/bin/Copy from another build stage
RUNRUN apt-get update && apt-get install -y curlExecute command — creates new layer
RUN chainRUN cmd1 && cmd2 && cmd3Chain commands to reduce layer count
ENVENV APP_ENV=production PORT=8080Set environment variables in image
ARGARG VERSION=1.0Build-time variable (not in final image)
EXPOSEEXPOSE 8080Document which port the app listens on
USERUSER nobody / USER 1000:1000Run as non-root for security
CMDCMD ["python", "app.py"]Default command to run (can be overridden)
ENTRYPOINTENTRYPOINT ["/docker-entrypoint.sh"]Fixed command — CMD becomes arguments
HEALTHCHECKHEALTHCHECK CMD curl -f http://localhost/health || exit 1Container health check
LABELLABEL maintainer='team@mywebuniversity.com'Add metadata to image
.dockerignore.git node_modules __pycache__ *.pycExclude files from build context
Layer cachingOrder: rarely-changed → often-changedCOPY requirements.txt before COPY . to cache pip install
AlpineFROM python:3.12-alpineSmallest base — 5MB, but uses musl libc
slimFROM python:3.12-slimDebian minimal — good balance of size and compatibility
distrolessFROM gcr.io/distroless/python3No shell, no package manager — most secure
Pinned versionsFROM ubuntu:22.04 not FROM ubuntu:latestPin exact versions for reproducibility
Non-rootUSER nobody after installing packagesNever run containers as root in production
SecretsUse BuildKit secrets: --secret id=mysecret,src=.secretNever COPY secret files into image

💡 Example

# ── Production-ready Dockerfile (Python API) ─────────────────
# Dockerfile
FROM python:3.12-slim AS base

# Metadata
LABEL maintainer="team@mywebuniversity.com" \
      version="2.0" \
      description="MyWebUniversity API"

# Build-time variables
ARG ENVIRONMENT=production

# System dependencies (rarely change — cache first)
RUN apt-get update && apt-get install -y --no-install-recommends \
        curl \
        ca-certificates \
    && rm -rf /var/lib/apt/lists/*

# Python environment
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1

# App directory
WORKDIR /app

# Install Python deps FIRST (separate layer — cached unless requirements changes)
COPY requirements.txt .
RUN pip install -r requirements.txt

# Copy application code (changes most often — last layer)
COPY src/ ./src/
COPY config/ ./config/

# Create non-root user AFTER installing packages
RUN addgroup --system app && adduser --system --ingroup app app
USER app

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD curl -f http://localhost:8080/health || exit 1

EXPOSE 8080
CMD ["python", "-m", "uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8080"]

# ── Multi-stage: Go binary ────────────────────────────────────
# Stage 1: Build
FROM golang:1.22-alpine AS builder
WORKDIR /build
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
FROM scratch
COPY --from=builder /build/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]
# Final image: just the binary — typically <10MB

# ── Multi-stage: Node.js ──────────────────────────────────────
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:20-alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN npm run build

FROM node:20-alpine AS runtime
WORKDIR /app
RUN addgroup -g 1001 nodejs && adduser -u 1001 -G nodejs nextjs
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
USER nextjs
EXPOSE 3000
CMD ["npm", "start"]

# ── .dockerignore ─────────────────────────────────────────────
cat > .dockerignore << 'EOF'
.git
.gitignore
.github
node_modules
__pycache__
*.pyc
*.pyo
*.egg-info
.env
.env.*
*.md
tests/
docs/
Dockerfile*
docker-compose*
.dockerignore
EOF

# Build commands
docker build -t mywebuniversity/api:latest .
docker build --target builder -t myapp-builder .  # build only one stage
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest . --push

← Docker Basics  |  🏠 Index  |  Docker Compose →