ESC
Type to search guides, tutorials, and reference documentation.
Verified by Garnet Grid

Container Security: Hardening Docker in Production

Secure your container pipeline from image building to runtime. Covers minimal base images, vulnerability scanning, non-root execution, runtime security policies, secrets injection, and the defense-in-depth approach that prevents container breakouts.

Containers are not security boundaries. They share the host kernel. A container running as root with unrestricted capabilities is one kernel exploit away from owning the entire host — along with every other container on it.

Most teams treat Docker as a packaging format and ignore the security implications. This guide covers how to build, ship, and run containers that are hardened against the attacks that actually happen in production.


Image Security: Start Before Runtime

Minimal Base Images

Base ImageSizePackagesAttack Surface
ubuntu:24.0478MB200+Large — shells, package managers, utilities
python:3.121.0GB400+Very large — full Debian with build tools
python:3.12-slim130MB100+Medium — reduced Debian
python:3.12-alpine50MB25Small — musl libc (compatibility issues)
gcr.io/distroless/python352MB~5Minimal — no shell, no package manager
scratch0MB0None — only your binary
# ✅ Multi-stage build with distroless runtime
# Stage 1: Build
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --target=/app/deps -r requirements.txt
COPY . .

# Stage 2: Runtime (minimal, no shell, no package manager)
FROM gcr.io/distroless/python3-debian12
WORKDIR /app
COPY --from=builder /app/deps /app/deps
COPY --from=builder /app/src /app/src
ENV PYTHONPATH=/app/deps
USER nonroot:nonroot
EXPOSE 8080
CMD ["src/main.py"]

Never Run as Root

# ❌ Bad: runs as root by default
FROM python:3.12-slim
COPY . /app
CMD ["python", "app.py"]

# ✅ Good: explicit non-root user
FROM python:3.12-slim

# Create non-root user
RUN groupadd --gid 1001 appuser && \
    useradd --uid 1001 --gid 1001 --shell /bin/false appuser

WORKDIR /app
COPY --chown=appuser:appuser . .

USER appuser
CMD ["python", "app.py"]

Image Scanning

Scan every image before it reaches production. Automate this in CI/CD so no unscanned image deploys.

# CI pipeline: scan before push
steps:
  - name: Build image
    run: docker build -t myapp:$SHA .

  - name: Scan for vulnerabilities
    run: |
      trivy image --severity HIGH,CRITICAL \
        --exit-code 1 \
        --ignore-unfixed \
        myapp:$SHA

  - name: Push (only if scan passes)
    run: docker push registry.example.com/myapp:$SHA
ScannerWhat It FindsSpeed
TrivyCVEs in OS packages + app dependenciesFast
GrypeCVEs in OS packages + app dependenciesFast
Snyk ContainerCVEs + license issues + configurationMedium
Docker ScoutCVEs with remediation adviceMedium

Runtime Security

Read-Only File System

# Kubernetes: read-only root filesystem
securityContext:
  readOnlyRootFilesystem: true
  runAsNonRoot: true
  runAsUser: 1001
  allowPrivilegeEscalation: false
  capabilities:
    drop:
      - ALL

# If the app needs to write (logs, temp files):
volumeMounts:
  - name: tmp
    mountPath: /tmp
  - name: logs
    mountPath: /var/log/app
volumes:
  - name: tmp
    emptyDir: {}
  - name: logs
    emptyDir: {}

Security Context Checklist

SettingValueWhy
runAsNonRoottruePrevents root execution
readOnlyRootFilesystemtruePrevents file system modification
allowPrivilegeEscalationfalsePrevents gaining extra privileges
capabilities.dropALLRemoves all Linux capabilities
capabilities.addOnly what’s neededPrinciple of least privilege
seccompProfileRuntimeDefaultRestrict system calls

Secrets in Containers

MethodSecurity LevelComplexity
Environment variablesLow (visible in docker inspect)Low
Docker secretsMedium (encrypted at rest, swarm only)Low
Mounted volumes from VaultHigh (dynamic, rotated)Medium
Init container + sidecarHighest (secrets never on disk)High
# ❌ Never: secrets in environment variables in manifests
env:
  - name: DATABASE_URL
    value: "postgres://admin:password123@db:5432/production"

# ✅ Better: reference Kubernetes secrets
env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: database-credentials
        key: url

# ✅ Best: External Secrets Operator + Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
spec:
  secretStoreRef:
    name: vault-backend
  target:
    name: database-credentials
  data:
    - secretKey: url
      remoteRef:
        key: production/database
        property: connection_string

Supply Chain Security

PracticeWhat It Prevents
Pin image digests (not just tags)Malicious tag overwrite
Sign images (Cosign, Notary)Unauthorized image deployment
Use private registryPulling compromised public images
Audit base imagesKnown vulnerabilities in base layer
SBOM generationUnknown components in your images
# ❌ Mutable tag — could change underneath you
FROM python:3.12-slim

# ✅ Pinned digest — immutable, verifiable
FROM python:3.12-slim@sha256:abc123def456...

Implementation Checklist

  • Use minimal base images (distroless or slim), never full OS images in production
  • Run all containers as non-root users
  • Enable read-only root filesystem for all production containers
  • Drop ALL Linux capabilities, add back only what is needed
  • Scan every image in CI/CD — block deployment on HIGH/CRITICAL CVEs
  • Never store secrets in environment variables or Dockerfiles
  • Pin base image versions by digest, not just tag
  • Use multi-stage builds to exclude build tools from runtime images
  • Set resource limits (CPU, memory) to prevent container resource abuse
  • Sign images and verify signatures before deployment
Jakub Dimitri Rezayev
Jakub Dimitri Rezayev
Founder & Chief Architect • Garnet Grid Consulting

Jakub holds an M.S. in Customer Intelligence & Analytics and a B.S. in Finance & Computer Science from Pace University. With deep expertise spanning D365 F&O, Azure, Power BI, and AI/ML systems, he architects enterprise solutions that bridge legacy systems and modern technology — and has led multi-million dollar ERP implementations for Fortune 500 supply chains.

View Full Profile →