DEV Community

Ramer Lacida
Ramer Lacida

Posted on

The Ultimate Checklist for Docker Deployments on Production

Why a Checklist Matters

Deploying Docker containers to production can feel like assembling a jigsaw puzzle blindfolded. One missing piece—an unpinned base image, an exposed port, or a container that never shuts down—can turn a smooth rollout into a firefighting session. A concise checklist forces you to verify the most common failure points before you press Deploy.


Pre‑flight: Prepare Your Image

Base Image Selection

  • Choose an official, minimal image (e.g., node:18-alpine or python:3.11-slim).
  • Avoid latest tags; they introduce nondeterministic builds.

Pinning Versions

# Dockerfile example for a Node.js API
FROM node:18.17-alpine AS builder
WORKDIR /app

# Pin npm version
RUN npm install -g npm@9.8.1

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

FROM node:18.17-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
Enter fullscreen mode Exit fullscreen mode
  • Why? The exact version numbers lock down the attack surface and guarantee reproducible builds across CI runners.

Secure the Runtime

Run as a Non‑Root User

# Add a low‑privilege user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
Enter fullscreen mode Exit fullscreen mode
  • Never run your app as root. This limits the impact of a container breakout.

Read‑Only Filesystem

Add the --read-only flag when you start the container, and mount only the directories that truly need write access.

docker run -d \
  --read-only \
  -v /app/tmp:/tmp:rw \
  mycompany/api:1.2.3
Enter fullscreen mode Exit fullscreen mode
  • Protects against accidental overwrites and makes your container behave more like a stateless micro‑service.

Networking & Service Discovery

  • Expose only needed ports – use EXPOSE in the Dockerfile and -p/--publish at runtime for the exact mapping.
  • Healthchecks – let orchestrators know when a container is ready.
HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:3000/health || exit 1
Enter fullscreen mode Exit fullscreen mode
  • Document the expected inbound/outbound traffic in a short table:
Port Protocol Purpose
3000 TCP HTTP API
5432 TCP Database (only internal)

Observability

  1. Structured Logging – output JSON to stdout/stderr. Most log aggregators (e.g., Loki, Papertrail) parse it automatically.
  2. Metrics – expose Prometheus metrics on a dedicated endpoint.
  3. Tracing – inject a trace ID at the entry point and propagate it downstream.
# Example: expose Prometheus metrics at /metrics
ENV METRICS_PORT=9100
EXPOSE 9100
Enter fullscreen mode Exit fullscreen mode

Graceful Shutdown with systemd

When Docker runs under a systemd service, you can control stop timeouts and ensure dependent services are notified.

# /etc/systemd/system/myapp.service
[Unit]
Description=My Dockerized API
After=network-online.target
Wants=network-online.target

[Service]
Restart=always
# Pull the latest image before each start (optional)
ExecStartPre=/usr/bin/docker pull mycompany/api:1.2.3
ExecStart=/usr/bin/docker run \
  --rm \
  --name myapp \
  --read-only \
  -v /app/tmp:/tmp:rw \
  -p 3000:3000 \
  mycompany/api:1.2.3
ExecStop=/usr/bin/docker stop -t 30 myapp
TimeoutStopSec=40

[Install]
WantedBy=multi-user.target
Enter fullscreen mode Exit fullscreen mode
  • TimeoutStopSec gives the container a 30‑second grace period to finish in‑flight requests before docker kill is forced.

CI/CD Integration

A minimal GitHub Actions workflow that builds, scans, and pushes the image:

name: CI
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: mycompany/api:${{ github.sha }}
      - name: Scan image
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: mycompany/api:${{ github.sha }}
          severity: HIGH,CRITICAL
Enter fullscreen mode Exit fullscreen mode
  • The scan step catches known CVEs before they reach production.

Final Thoughts

Running Docker in production isn’t just about writing a Dockerfile. It’s a disciplined process that spans image hygiene, runtime hardening, networking, observability, and automation. By ticking off each item on this checklist you’ll reduce the odds of a surprise outage and keep your services performant and secure. If you’re looking for a partner that can help you audit your container pipeline or provide managed Kubernetes hosting, consider checking out https://lacidaweb.com for a no‑pressure conversation.

Top comments (0)