DEV Community

Ramer Lacida
Ramer Lacida

Posted on

The Ultimate Checklist for Building a Secure Docker CI/CD Pipeline

Introduction

If you’re a DevOps lead tasked with rolling out a Docker‑centric CI/CD pipeline, a solid checklist can keep you from missing critical steps. This guide walks you through the essential pieces—from image hardening to monitoring—so you can ship code fast and keep your environment safe.


1. Repository Hygiene

  • Branch protection: Require pull‑request reviews and status checks before merging to main.
  • Secret scanning: Enable tools like GitGuardian or TruffleHog to catch API keys early.
  • Commit linting: Enforce conventional commits to keep changelogs readable.

2. Dockerfile Best Practices

A well‑crafted Dockerfile reduces attack surface and improves build speed.

# Use an official, minimal base image
FROM python:3.12-slim-alpine AS builder

# Set a non‑root user early
ARG USERNAME=appuser
ARG USER_UID=1001
ARG USER_GID=$USER_UID
RUN addgroup -g $USER_GID $USERNAME \
    && adduser -u $USER_UID -G $USERNAME -s /bin/sh -D $USERNAME

# Install only build‑time dependencies
RUN apk add --no-cache gcc musl-dev libffi-dev

# Copy source code and install runtime deps
COPY requirements.txt /app/
WORKDIR /app
RUN pip install --no-cache-dir -r requirements.txt

# Switch to non‑root user for runtime
USER $USERNAME

# Final stage – slim runtime image
FROM python:3.12-slim-alpine
COPY --from=builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages
COPY --from=builder /app /app
WORKDIR /app
CMD ["python", "-m", "myapp"]
Enter fullscreen mode Exit fullscreen mode

Key takeaways:

  • Multi‑stage builds keep the final image lean.
  • Run as a non‑root user.
  • Pin exact base image versions.
  • Remove build‑time packages before the final stage.

3. Image Scanning & Signing

  • Scanning: Integrate tools like Trivy or Clair into your CI pipeline. Example GitHub Actions step:
- name: Scan Docker image
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
    severity: HIGH,CRITICAL
Enter fullscreen mode Exit fullscreen mode
  • Signing: Use Docker Content Trust (DCT) or Cosign to sign images before pushing to your registry.

4. CI Workflow Structure

Stage Purpose Typical Tool
Lint Static analysis of Dockerfile and code Hadolint, ESLint
Test Unit & integration tests pytest, Jest
Build Create image, run scans Docker, BuildKit
Push Push signed image to registry Docker, Cosign
Deploy Deploy to staging/production Argo CD, Helm

Sample GitHub Actions CI

name: CI
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Lint Dockerfile
        uses: hadolint/hadolint-action@v2
        with:
          dockerfile: Dockerfile
      - name: Run tests
        run: |
          python -m venv .venv
          . .venv/bin/activate
          pip install -r requirements.txt
          pytest
      - name: Build image
        run: |
          docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} .
      - name: Scan image
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
      - name: Sign image
        run: |
          cosign sign --key ${{ secrets.COSIGN_KEY }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
Enter fullscreen mode Exit fullscreen mode

5. Runtime Hardening

  • Resource limits: Define CPU and memory caps in docker-compose.yml or Kubernetes manifests.
  • Read‑only file systems: Where possible, mount volumes as read‑only.
  • Seccomp & AppArmor: Apply default profiles, then tighten as needed.
  • Network segmentation: Use user‑defined bridge networks to isolate containers.
services:
  web:
    image: myorg/webapp:latest
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
    read_only: true
    networks:
      - frontend

networks:
  frontend:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

6. Monitoring & Observability

  • Metrics: Export Docker daemon metrics to Prometheus via cadvisor.
  • Logs: Ship container stdout/stderr to Loki or Elastic Stack using the json-file driver.
  • Alerting: Set up alerts for high CPU, memory spikes, or image vulnerabilities.
# docker-compose snippet for cAdvisor
services:
  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    ports:
      - "8080:8080"
    volumes:
      - /var/run:/var/run:ro
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
Enter fullscreen mode Exit fullscreen mode

7. Backup & Disaster Recovery

  • Registry backups: Periodically docker save critical images and store them in an immutable S3 bucket.
  • Stateful data: Use volume snapshots (e.g., docker volume create --driver local ...) and store snapshots off‑site.
  • Runbooks: Document a step‑by‑step rollback procedure, including image tag rollback and database restore.

8. Periodic Review Checklist

  • [ ] Are all base images up‑to‑date with security patches?
  • [ ] Have you removed any unnecessary packages from the final image?
  • [ ] Do all images pass Trivy/Clair scans with no HIGH or CRITICAL findings?
  • [ ] Are runtime containers running with least‑privilege user IDs?
  • [ ] Is resource throttling configured for each service?
  • [ ] Are logs and metrics being shipped to a central store?
  • [ ] Have you tested a full restore from your backup store in the last month?

Conclusion

Building a Docker‑centric CI/CD pipeline isn’t just about automation; it’s about embedding security and observability into every step. By ticking off the items above, you’ll reduce the risk of supply‑chain attacks, keep performance predictable, and give your team confidence that releases are both fast and safe.

If you’re looking for a partner that can help audit your pipeline or provide managed Docker hosting, consider checking out https://lacidaweb.com for a low‑key, no‑pressure conversation.

Top comments (0)