← Course Index

Docker in Production

~25 min · Docker · Docker Cheat Sheet →

Ref
Primary Source
Docker Docs — Security Best Practices

Official guide for hardening Docker in production environments. docs.docker.com/security →

Image Tagging Strategy

Never use latest in production. You need to know exactly what version is running and be able to roll back to any previous version.

# Bad: latest is a moving target — no reproducibility
docker pull myapp:latest

# Good: tag by git commit SHA (immutable, traceable)
GIT_SHA=$(git rev-parse --short HEAD)
docker build -t myapp:${GIT_SHA} .
docker push myapp:${GIT_SHA}

# Also tag as "latest" for convenience (not for deployment)
docker tag myapp:${GIT_SHA} myapp:latest

# In CI/CD: tag by version + sha
docker build -t myapp:v1.2.3-abc1234 .

Container Registries

A registry stores and serves your Docker images. In production you need a private registry — you don't want your images public.

RegistryBest forNotes
Docker HubPublic open-source imagesFree tier limits pulls. Private repos cost money.
AWS ECRAWS deploymentsNative integration with ECS/EC2. Pay per storage.
GitHub GHCRGitHub Actions workflowsFree for public, integrated with GHCR token auth.
Google Artifact RegistryGCP deploymentsReplaces old GCR.
# Authenticate with AWS ECR
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin \
  123456789.dkr.ecr.us-east-1.amazonaws.com

# Push to ECR
docker tag myapp:abc1234 \
  123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:abc1234
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:abc1234

# Authenticate with GHCR (GitHub Container Registry)
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
docker tag myapp:abc1234 ghcr.io/username/myapp:abc1234
docker push ghcr.io/username/myapp:abc1234

Production Security Hardening

1. Run as Non-Root (Critical)

By default, containers run as root. If an attacker exploits your app, they get root inside the container. Always use a non-root USER in your Dockerfile (covered in Lesson 06).

2. Read-Only Filesystem

# Container can't write to filesystem (except explicit volumes)
docker run --read-only \
  -v /tmp:/tmp \
  -v /app/logs:/app/logs \
  myapp:abc1234

3. Limit Resources

# Prevent a runaway container from consuming all server resources
docker run \
  --memory="512m" \
  --cpus="0.5" \
  myapp:abc1234

# In docker-compose.yml:
services:
  api:
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.50'

4. Scan Images for Vulnerabilities

# Docker Scout (built-in)
docker scout quickview myapp:abc1234
docker scout cves myapp:abc1234

# Trivy (open source, use in CI/CD)
trivy image myapp:abc1234

5. Use .dockerignore Religiously

Already covered in Lesson 06. Never skip this.

Logging & Health Checks

# Configure log rotation (prevents disk fill)
docker run \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  myapp:abc1234

# In docker-compose.yml:
services:
  api:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"
💡 Health check in Compose

Docker's health check lets orchestrators know if your app is truly healthy, not just started. In production, always expose a /health endpoint in your API and reference it in the HEALTHCHECK instruction (covered in Lesson 06) and Compose file.

Check Your Understanding

1. You're deploying to production and need to roll back to the version from 3 deploys ago. You used `latest` tags. What's the problem?
2. Your Node.js container is running as root. An attacker exploits an XSS vulnerability and gets code execution. What's the risk compared to running as a non-root user?
3. Which image tag strategy gives you the best traceability and rollback capability in CI/CD?