Production Docker Checklist: Security, Performance, Storage, and Deployments
AI generated
Docker · Security · Performance · Storage · Deployment
Production Docker Checklist:
Security, Performance, Storage, and Deployments

Containers on a developer laptop and containers in production are two fundamentally different things. This Docker checklist covers every mandatory category: security hardening, performance limits, storage protection, and repeatable deployment procedures, with concrete commands for each item.

18 min read Security · Healthchecks · Resource Limits · Volumes · Zero-Downtime Docker 24+ · Docker Compose · Linux

1. Why you need a Docker checklist for production

Docker simplifies deployment considerably, but it also creates new attack surfaces and operational risks that are easy to overlook without a systematic Docker checklist. A container running as the root user, an image without version pinning, and a Compose stack without memory limits are all tolerable during development. In production, the same oversights translate into potential security holes, non-reproducible deployments, and systems that destabilize other services on the same host during traffic spikes.

A structured Docker checklist for production environments is not bureaucratic overhead. It is a safety net that prevents critical configuration items from being forgotten under time pressure. It covers four mandatory categories: security (hardening images and the runtime environment), performance (resource limits and monitoring), storage (volumes, backups, data protection), and deployment (reproducibility, zero downtime, rollbacks). Teams that apply this Docker checklist consistently see a measurable reduction in security incidents and unplanned downtime.

2. Image security: non-root, minimal images, and signatures

The first and most impactful item on any security Docker checklist: containers must never run as the root user in production. A process running as root inside a container can reach the host directly in the event of a container escape or a kernel bug. The Dockerfile directive USER and creating a dedicated application user with minimal permissions are mandatory. Distroless images and Alpine-based minimal images further reduce the attack surface: fewer installed packages mean fewer potential CVEs.

Version pinning, meaning the use of specific image tags with a SHA256 digest instead of floating tags like latest, is another mandatory item. FROM nginx:1.27.4@sha256:abc123... guarantees that a rebuild uses exactly the same base image. latest can change between builds and silently introduce breaking changes. Image scanning with tools like Trivy, Grype, or Docker Scout should run automatically in the CI/CD pipeline and block builds that contain known critical CVEs. Together, these items form the foundation of the image security section of every Docker checklist.


# Dockerfile: production-hardened image following the Docker checklist

# Pin base image with digest, no floating tags in production
FROM php:8.4-fpm-alpine@sha256:abc123def456...

# Install only necessary packages to minimize attack surface
RUN apk add --no-cache \
    libpng-dev \
    libzip-dev \
 && docker-php-ext-install pdo_mysql zip gd \
 && rm -rf /var/cache/apk/*

# Create dedicated non-root application user
RUN addgroup -g 1001 -S appgroup \
 && adduser -u 1001 -S appuser -G appgroup

WORKDIR /var/www/html

# Copy application files with correct ownership
COPY --chown=appuser:appgroup . .

# Drop to non-root user: never run as root in production
USER appuser

# Expose only the port the application needs
EXPOSE 9000

# Healthcheck: enables Docker to detect unhealthy containers
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
  CMD php-fpm-healthcheck || exit 1

3. Runtime security: capabilities, seccomp, and read-only filesystems

Linux capabilities allow fine-grained privilege assignment beyond the simple root/non-root split. Docker containers start by default with a reduced but still fairly large capability set. The Docker checklist recommends using --cap-drop=ALL to remove every capability and then adding back only what is genuinely needed with --cap-add. Most web applications need no capabilities at all; database containers occasionally need CAP_SYS_NICE for scheduling priorities.

A read-only filesystem via --read-only prevents a compromised container from persistently writing malware to disk. For directories that must remain writable, such as temporary files or logs, tmpfs mounts are configured. Seccomp profiles restrict which kernel syscalls a container can invoke, significantly reducing the attack surface against kernel exploits. Docker ships a default seccomp profile that already blocks most dangerous syscalls; a custom profile tailored to known workloads can tighten this restriction even further. These runtime security measures are core items of the Docker checklist for production systems.

4. Resource limits: CPU, memory, and pids

Without resource limits, a single container can exhaust all of a host's resources through a memory leak or an infinite loop and destabilize other services in the process. The Docker checklist mandates explicit memory and CPU limits for every production container. The memory limit (mem_limit in Compose, or --memory with docker run) prevents a container from growing without bound. The memory swap limit should be set equal to the memory limit to prevent swapping entirely, since swapping under containers slows down the performance of the entire host.

CPU limits via cpus and cpu_shares ensure that critical services (database, API) can claim their CPU share against less critical processes (cron jobs, batch processing). The PID limit (--pids-limit) prevents fork bombs, which can crash a host through exponential process growth. Monitoring actual resource usage with docker stats or Prometheus/cAdvisor provides the data needed to size limits correctly; limits set too tight lead to OOM kills and are just as dangerous as having no limits at all.


# docker-compose.yml: resource limits and security hardening checklist

services:
  app:
    image: myapp:1.2.3@sha256:abc123...
    user: "1001:1001"                    # Non-root user
    read_only: true                      # Read-only filesystem
    tmpfs:
      - /tmp:size=64m,mode=1777         # Writable temp dir in RAM
      - /var/run:size=10m
    cap_drop:
      - ALL                              # Drop all capabilities
    cap_add:
      - NET_BIND_SERVICE                 # Only if port < 1024 needed
    security_opt:
      - no-new-privileges:true           # Prevent privilege escalation
      - seccomp:./seccomp-profile.json
    deploy:
      resources:
        limits:
          cpus: "1.0"                    # Max 1 CPU core
          memory: 512M                   # Hard memory limit
          pids: 100                      # Prevent fork bombs
        reservations:
          cpus: "0.25"
          memory: 128M
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

5. Healthchecks and restart policies

Healthchecks are a frequently forgotten item on the Docker checklist with a large operational impact. Without a healthcheck, a container is considered healthy as soon as the main process is running, regardless of whether the application actually responds to requests. A web server can be running and still be in a state where it answers every request with HTTP 500. With a healthcheck that tests a defined endpoint, Docker can detect unhealthy containers and, with the right configuration, restart them automatically.

Restart policies define how Docker behaves when a container fails. restart: unless-stopped is the right choice for most production services: the container starts automatically after a crash or a host reboot, but a deliberate manual stop (for maintenance, for example) is respected. restart: always ignores manual stops and always starts the container the next time the Docker daemon starts. Combining a healthcheck with a restart policy that has a backoff limit prevents a crash-looping container image from overwhelming the host with rapid restarts. Both items are mandatory on every Docker checklist for production systems.

6. Storage: volumes, backups, and data protection

The storage section of the Docker checklist starts with a simple rule: all persistent data must live in named volumes. Container layer data is lost on every image update. Named volumes with external: true in Compose files protect production data from accidental deletion via docker compose down -v. Every volume holding production data should carry a backup=required label and be included in an automated backup routine.

Sensitive data, such as passwords, API keys, and TLS certificates, must never be hardcoded as environment variables in a Compose file or baked into an image. Docker Secrets (in Swarm mode) or reading values from .env files that are not committed to the repository are the correct patterns. The Docker checklist also recommends mounting TLS certificates as read-only bind mounts or volumes, never copying them into the image, because an image rebuild does not automatically renew the certificate and a registry image exposes every secret it contains.

7. Network segmentation and secrets management

Network segmentation is a core item on the Docker checklist: every service should be able to reach only the services it genuinely needs to talk to. In Docker Compose this is achieved with multiple named networks. A frontend network connects the reverse proxy to the web application. A backend network connects the web application to the database and cache. The database ends up fully isolated from the reverse proxy and the internet, without a single explicit firewall rule. This is the principle of least network access, a direct equivalent of the principle of least privilege at the file level.

For secrets, the Docker checklist recommends strict rules: no passwords in environment variables that would be visible in docker inspect output, and no secrets baked into images. Instead, use secrets as files under /run/secrets/ (Docker Swarm secrets), a bind mount from a protected host directory, or an external secret store such as HashiCorp Vault or AWS Secrets Manager. The _FILE suffix convention for environment variables, a pattern common in official images, lets you read a variable's value from a file instead of specifying it directly.


# docker-compose.yml: network segmentation and secrets management

services:
  nginx:
    image: nginx:1.27.4-alpine@sha256:abc123...
    networks:
      - frontend           # Only in frontend network, no DB access
    ports:
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - tls_certs:/etc/nginx/certs:ro      # TLS certs via read-only volume

  app:
    image: myapp:1.2.3@sha256:def456...
    networks:
      - frontend           # Reachable from nginx
      - backend            # Can reach db and redis
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

  db:
    image: mysql:8.0.39@sha256:ghi789...
    networks:
      - backend            # Isolated: not reachable from frontend
    environment:
      MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
    secrets:
      - db_root_password

networks:
  frontend:
  backend:
    internal: true         # No external internet access for backend network

secrets:
  db_password:
    file: ./secrets/db_password.txt
  db_root_password:
    file: ./secrets/db_root_password.txt

8. Zero-downtime deployments and rollbacks

Reproducible deployments without downtime are a critical item on the Docker checklist. The basic pattern for zero downtime with Docker Compose: pull the new image, restart the service with --no-deps --build, wait for the healthcheck to pass, then switch traffic over. Docker Swarm supports rolling updates natively: every Swarm service can be updated on a rolling basis with defined update_config parameters, so a defined minimum share of instances always stays available.

Rollbacks matter just as much as forward deployments, and they are frequently forgotten on the Docker checklist. Version pinning images (never latest) is a prerequisite for reliable rollbacks: only when the previous image is still available in the registry and can be referenced unambiguously can a rollback happen within seconds. A deployment pipeline that automatically verifies the new container is healthy before stopping the old one, and rolls back automatically on failure, is the goal of every mature container deployment strategy. This safeguard is mandatory on every complete Docker checklist.

9. Docker checklist: development vs. production compared

The differences between a development and a production configuration are substantial. A complete Docker checklist makes these differences explicit and visible.

Checklist item Development Production (mandatory) Risk without it
User Often root for simplicity Non-root (1001:1001) Container escape means host compromise
Image tags latest is acceptable Pin with digest Non-reproducible builds, breaking changes
Memory limits Often not set mem_limit + memswap_limit Memory leak destabilizes the host
Secrets Hardcoded in .env Secret files, _FILE variables Secrets visible in logs and docker inspect
Healthchecks Rarely configured Mandatory for every service Broken services stay invisible in production

The table makes it clear: almost every item on the production Docker checklist is deliberately simplified during development. That is acceptable as long as the differences are documented and systematically accounted for at deployment time. Separate Compose override files (docker-compose.yml for the base configuration, docker-compose.prod.yml for production overrides) are the recommended pattern for keeping development and production configuration cleanly separated.

Mironsoft

Docker security audits, production hardening, and deployment automation

Ready to work through the Docker checklist for your production environment?

We run complete Docker security audits, harden existing Compose stacks against the checklist, and build zero-downtime deployment pipelines.

Security audit

A systematic review against the Docker checklist with a prioritized action plan

Hardening

Implementing non-root users, read-only filesystems, capability drops, seccomp, and resource limits

Deployment pipeline

Building zero-downtime deployments with automatic health verification and rollback

10. Summary

The Docker checklist for production environments spans four equally important categories: security, performance, storage, and deployments. Non-root users, digest-pinned image tags, capability drops, and a read-only filesystem are the mandatory security items. Memory and CPU limits, PID limits, and properly configured monitoring are the mandatory performance items. Named volumes with external: true, automated backups with a retention policy, and secrets management without hardcoding are the mandatory storage items.

For deployments, the Docker checklist requires: a healthcheck for every service, a restart policy with a backoff limit, network segmentation across multiple named networks, and zero-downtime deployment procedures with automatic rollback. Separate Compose override files for production and development keep these differences explicit and prevent development shortcuts from leaking into production. Consistently applying this Docker checklist is the difference between running containers and running containers professionally.

Production Docker checklist: the essentials at a glance

Mandatory security items

Non-root USER, image digest pinning, cap_drop: ALL, read_only: true, no-new-privileges:true, image scanning in CI.

Mandatory performance items

Memory and CPU limits for every service. PID limit against fork bombs. Healthcheck with start_period for slow-starting services.

Mandatory storage items

Named volumes with external: true. Backup automation with retention. Secrets never in environment variables or images, always as files.

Mandatory deployment items

Zero downtime through rolling updates or blue-green deployments. Automatic rollback on a failed healthcheck. Separate Compose files for dev and prod.

11. FAQ: Production Docker Checklist

1Most important items on the Docker checklist?
Non-root users, digest pinning, capability drops, memory limits, healthchecks, external named volumes, secrets management, and zero-downtime deployments with rollback.
2Why no root in production?
A container escape with root access means direct host access. Non-root limits the damage to the container scope. USER 1001:1001 in the Dockerfile is the simplest mandatory item.
3What happens without memory limits?
A memory leak can destabilize the host. With mem_limit plus memswap_limit equal to mem_limit, the container gets an OOM kill before it endangers other services.
4always vs. unless-stopped?
unless-stopped respects a manual stop for maintenance. always ignores it. unless-stopped is the better choice for production services.
5Protecting secrets in Docker Compose?
Never as plaintext env vars. Use _FILE variables or Docker Secrets instead. The secret is then not visible in docker inspect, logs, or image layers.
6What does network segmentation get you?
Isolation without firewall rules: the DB is reachable only for the app, not for the proxy or the internet. internal: true on backend networks blocks outbound connections.
7Zero downtime with Docker Compose?
Pull the new image, start a new instance, wait for the healthcheck, then stop the old container. The healthcheck must be configured so Docker recognizes the ready state.
8What is image digest pinning?
FROM image:tag@sha256:... guarantees exactly the same base image every time. latest can change and silently introduce breaking changes or new CVEs.
9Testing a production configuration locally?
docker compose -f docker-compose.yml -f docker-compose.prod.yml config shows the merged configuration. --dry-run runs a trial without starting containers.
10What does docker compose config check?
All environment variables that are set, existing external volumes and networks, correct image tags, and configured resource limits. Errors become visible before the deployment.