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.
Table of Contents
- 1. Why you need a Docker checklist for production
- 2. Image security: non-root, minimal images, and signatures
- 3. Runtime security: capabilities, seccomp, and read-only filesystems
- 4. Resource limits: CPU, memory, and pids
- 5. Healthchecks and restart policies
- 6. Storage: volumes, backups, and data protection
- 7. Network segmentation and secrets management
- 8. Zero-downtime deployments and rollbacks
- 9. Docker checklist: development vs. production compared
- 10. Summary
- 11. FAQ
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.