Docker in CI/CD Pipelines
In modern continuous integration and delivery (CI/CD), every Git commit triggers an automated pipeline that tests, compiles, security-scans, and publishes container images. In this chapter, learn how to configure enterprise GitHub Actions workflows with BuildKit distributed caching and automated vulnerability quality gates.
1. The Automated Container Delivery Lifecycle
An enterprise container delivery pipeline enforces strict quality gates before an image ever enters a production registry:
2. Production GitHub Actions Workflow (.github/workflows/deploy.yml)
This hardened workflow leverages GitHub Actions Cache (type=gha) to reuse layer caches across runner instances (dropping build times from 6 minutes to under 30 seconds), automatically extracts SemVer tags, and breaks the build if CRITICAL security vulnerabilities are detected:
name: Build, Scan & Publish Production Container
on:
push:
branches: ["main"]
tags: ["v*.*.*"]
pull_request:
branches: ["main"]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-deliver:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
security-events: write
steps:
# Step 1: Checkout Git source code
- name: Checkout Repository
uses: actions/checkout@v4
# Step 2: Set up QEMU for multi-architecture emulation (ARM64 & AMD64)
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
# Step 3: Set up Docker Buildx with BuildKit
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# Step 4: Authenticate to GitHub Container Registry (GHCR)
- name: Log in to Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Step 5: Extract dynamic metadata, Git commit SHAs, and SemVer tags
- name: Extract Docker Metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,format=short
type=raw,value=latest,enable={{is_default_branch}}
# Step 6: Build image locally for security scanning
- name: Build Local Image for CVE Gate
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: local-scan-target:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# Step 7: Security Quality Gate (Fail pipeline on CRITICAL CVEs!)
- name: Run Trivy Vulnerability Scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: local-scan-target:latest
format: 'table'
exit-code: '1'
ignore-unfixed: true
severity: 'CRITICAL'
# Step 8: Build and push multi-architecture image to production registry
- name: Build and Push Multi-Platform Image
if: github.event_name != 'pull_request'
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max3. Ephemeral Integration Testing Services
Never mock databases when running end-to-end integration tests. GitHub Actions supports nativeDocker Service Containers to spin up real, disposable PostgreSQL or Redis instances for testing:
# Inside your test job definition:
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
POSTGRES_DB: integration_test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 2s
--health-retries 5Next Up: Production Deployment
In Chapter 18, discover how to deploy production containers to cloud VPS instances, configure zero-downtime rolling updates, and log drivers.
Chapter 18: Production Deployment →