E-NO Logo
EN FR
Docker production 7 Min Read

Docker production operations checklist with practical examples: practical implementation guide

calendar_today Published: 2026-07-13
update Last Updated: 2026-07-13
analytics SEO Efficiency: 97%
Technical guide illustration for Docker production operations checklist with practical examples: practical implementation guide.

This guide is a practical, copy-paste-friendly checklist for running Docker in production. It covers configuration, security, resource limits, networking, observability, backups, upgrades, and maintenance. Each section includes concrete examples you can adapt.

Intro

Running Docker in production is about repeatable builds, secure defaults, solid observability, and safe change management. The payoff is higher reliability with less rework and fewer surprises. Use this checklist to standardize your approach and accelerate delivery.

Workflow Overview

Follow these steps for each service:

  1. Build minimal, pinned images
  2. Configure runtime safely (user, caps, filesystem)
  3. Isolate networks and expose only what you need
  4. Set resource limits and healthchecks
  5. Wire logs and metrics with bounded retention
  6. Persist state with named volumes and test backups
  7. Upgrade with rollbacks, verify with probes
  8. Maintain hosts and artifacts on a schedule

Tip: small, observable steps reduce rework and make issues easier to isolate.

Configuration baseline

Checklist:

  • Multi-stage builds to keep images small
  • Pin base images and tools by version (or digest)
  • Non-root user with a fixed UID/GID
  • HEALTHCHECK in the image or Compose
  • Minimal files via a tight .dockerignore
  • Labels for provenance (org, version, vcs, build date)

Example Dockerfile (Go service):

# Build stage
FROM golang:1.22-alpine AS build
WORKDIR /src
RUN adduser -D -u 10001 app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags='-s -w' -o /out/app ./cmd/server

# Runtime stage
FROM alpine:3.20
RUN addgroup -g 10001 app \
  && adduser -D -u 10001 -G app app \
  && apk add --no-cache ca-certificates curl
WORKDIR /app
COPY --from=build /out/app /app/app
USER 10001:10001
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -fsS http://127.0.0.1:8080/health || exit 1
ENTRYPOINT ["/app/app"]

Security hardening

Checklist:

  • Run as a non-root UID/GID
  • Read-only root filesystem; temp write via tmpfs
  • Drop all Linux capabilities, add back only what is required
  • no-new-privileges to block privilege escalation
  • Avoid host PID/net namespaces and privileged mode
  • Keep secrets as files, not env vars

Example docker-compose.yml security defaults:

version: '3.9'
services:
  app:
    image: myorg/myapp:1.2.3
    user: '10001:10001'
    read_only: true
    tmpfs:
      - /tmp: rw, noexec, nosuid, size=64m
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges: true
    restart: always
    stop_grace_period: 30s

Secrets as files with Compose:

services:
  app:
    secrets:
      - source: db_password
        target: db_password
        mode: 0400
secrets:
  db_password:
    file: ./secrets/db_password.txt

Your app should read the secret at /run/secrets/db_password.

Resource limits and healthchecks

Checklist:

  • Add a container healthcheck
  • Set CPU and memory limits
  • Configure ulimits (nofile, nproc)
  • Use a sensible restart policy (always or unless-stopped)

Compose example (non-Swarm):

services:
  app:
    # Build or image here
    healthcheck:
      test: ['CMD', 'wget', '-q', '-O', '-', 'http://127.0.0.1:8080/health']
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 10s
    restart: always
    mem_limit: 512m
    cpus: '0.50'
    ulimits:
      nofile:
        soft: 65536
        hard: 65536
      nproc: 4096

Note: deploy.resources is for Swarm. For standalone Docker Compose, use mem_limit and cpus as shown.

Networking and ingress

Checklist:

  • Use a user-defined bridge network per app or domain
  • Map only required ports to the host
  • Keep internal services private on the app network
  • Terminate TLS at an ingress proxy (for example Nginx)

Example network isolation:

services:
  app:
    networks: [app-net]
  nginx:
    image: nginx:1.25
    depends_on: [app]
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf: ro
    networks: [app-net]
networks:
  app-net:
    driver: bridge

Minimal nginx.conf upstream example:

worker_processes auto;
events { worker_connections 1024; }
http {
  upstream app_upstream { server app:8080; }
  server {
    listen 80;
    location / {
      proxy_pass http://app_upstream;
      proxy_set_header Host $host;
      proxy_set_header X-Forwarded-For $remote_addr;
    }
  }
}

Logging and metrics

Checklist:

  • Use a consistent logging driver with rotation
  • Emit structured logs (JSON lines) from the app
  • Capture container-level CPU, memory, and restart counts

Compose logging example:

services:
  app:
    logging:
      driver: json-file
      options:
        max-size: '10m'
        max-file: '3'

Operational tips:

  • docker logs --since 10m app
  • docker stats app
  • docker inspect --format '{{.State.Health}}' app

Storage and backups

Checklist:

  • Use named volumes for state
  • Keep configuration and secrets outside the image
  • Document and test backup and restore

Create and back up a named volume:

# Create a volume
docker volume create mydata

# Backup to a timestamped tarball in the current dir
docker run --rm \
  -v mydata:/data \
  -v "$PWD:/backup" \
  busybox sh -c 'tar czf /backup/mydata-$(date +%F).tgz -C / data'

# List backups
ls -lh mydata-*.tgz

# Restore from a backup
docker run --rm \
  -v mydata:/data \
  -v "$PWD:/backup" \
  busybox sh -c 'tar xzf /backup/mydata-2026-07-13.tgz -C /'

For databases, prefer native tools (for example pg_dump, mysqldump) to ensure consistency.

Upgrades and rollbacks

Checklist:

  • Pin images by version or digest
  • Upgrade in small increments
  • Verify health and logs before switching traffic
  • Keep a fast rollback path

Pin by tag and digest in Compose:

services:
  nginx:
    image: nginx:1.25.5@sha256: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Rolling upgrade with two projects:

# Deploy next
docker compose -p myapp-next up -d
# Verify health
docker compose -p myapp-next ps
# Switch traffic at ingress (update upstream or DNS)
# Decommission previous
docker compose -p myapp-current down

Simple in-place upgrade:

docker compose pull
docker compose up -d
docker compose ps

Maintenance and troubleshooting

Checklist:

  • Monitor disk usage and prune safely
  • Rotate logs and keep bounds
  • Keep only necessary images and networks

Useful commands:

# Show space usage
docker system df

# List dangling images first
docker images -f dangling=true

# Prune safely (confirm prompt)
docker image prune

# Full prune of unused images and build cache (be careful)
docker image prune -af

# Inspect recent restarts
docker events --since 1h --filter 'event=restart'

Local Pilot Plan

Goal: prove the checklist on one stateless service with clear acceptance criteria.

Scope:

  • One service behind Nginx
  • Healthcheck, restart policy, mem and CPU limits
  • Log rotation via json-file driver
  • A small named volume and a tested backup

Steps:

  1. Implement the Compose file with security defaults, limits, and logging
  2. Add a health endpoint and verify docker ps shows healthy
  3. Simulate a crash and verify restart: docker kill -s SIGKILL app
  4. Capture baseline metrics with docker stats and startup time
  5. Back up and restore the volume, verify data integrity

Acceptance criteria:

  • Healthcheck passes within 10 seconds of container start
  • No container logs exceed 30 MB after rotation
  • Memory and CPU remain within configured limits under a basic load
  • Backup restore recreates the expected state

Common production pitfalls and fixes

  • Using :latest tags
  • Fix: pin tags or digests and update intentionally.
  • Running as root
  • Fix: create a non-root user and set user in image and Compose.
  • Missing healthchecks
  • Fix: add HEALTHCHECK in the image or Compose.
  • Unbounded logs
  • Fix: set json-file rotation or ship logs to a central store with retention.
  • Writing state to the container filesystem
  • Fix: use named volumes and verify backup-restore.
  • Exposing more ports than needed
  • Fix: keep internal services on a private network.
  • Overlooking ulimits
  • Fix: set nofile and nproc to match workload needs.
  • Relying on deploy.resources in non-Swarm Compose
  • Fix: use mem_limit and cpus for standalone Docker.

Conclusion

Adopt the checklist, start with the local pilot, and expand service by service. Keep changes small, measurable, and reversible. With secure defaults, clear limits, and proven backups, your Docker production operations will be safer, faster, and easier to troubleshoot.

Article Quality Score

Reader usefulness 97%
  • check_circle Reader-ready guide
  • check_circle Practical examples included
  • check_circle Clean SEO article URL