Container CI/CD: Docker, Kubernetes, and Registries
Containers have become the standard packaging format for cloud-native applications. Container CI/CD pipelines automate the build, scan, store, and deploy lifecycle for Docker images across Kubernetes and other container orchestration platforms. This guide covers Dockerfile optimization, multi-stage builds, registry management, Kubernetes deployment strategies in CI/CD, and the security considerations that protect containerized software supply chains.
Container Builds in CI/CD
Building container images within a CI/CD pipeline introduces unique challenges compared to compiling native binaries. Docker builds inherit the host system’s layer cache, consume significant disk and network resources, and produce images that must be stored and versioned in registries. Optimizing the container build process is essential for keeping pipeline times manageable.
Dockerfile Best Practices
A well-structured Dockerfile minimizes image size, build time, and security surface area. The foundational principle is layer ordering: place layers that change infrequently (base images, system dependencies) at the top of the Dockerfile, and layers that change often (application code) at the bottom. This maximizes cache reuse across builds.
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM base AS build
COPY . .
RUN npm run build
FROM nginx:alpine AS production
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80This multi-stage Dockerfile demonstrates three key practices:
- Base image pinning —
node:20-alpinespecifies an exact major version. Avoidnode:latest, which produces non-reproducible builds. - Dependency caching —
package*.jsonis copied before source code. As long as dependencies do not change, this layer is cached from previous builds. - Multi-stage build — the build stage includes the full Node toolchain. The production stage copies only the built artifacts into a minimal Nginx image, slashing the final image size from 1.2 GB to under 50 MB.
Multi-Stage Builds
Multi-stage builds are the single most effective optimization for container-based CI/CD. Each FROM statement starts a new stage, and COPY --from= selectively transfers artifacts between stages. The compiler, test framework, and development dependencies remain in earlier stages and are discarded from the final image.
Common multi-stage patterns include:
- Build → Test → Prod: Run tests in a stage with dev dependencies, then copy tested artifacts to a minimal runtime image.
- Base → Dependencies → App → Runtime: Cache dependency-heavy layers (Python’s
pip install, Node’snpm ci, Go’sgo mod download) before adding application code. - Language-specific toolchain → Thin runtime: Java applications compile with JDK in one stage, then run on a JRE-only image. Go binaries compile to static binaries and can run in
scratchordistrolessimages.
Docker Layer Caching in CI
CI environments start with a clean Docker build cache on every run unless caching is explicitly configured. Losing the cache means every Docker build downloads and installs all dependencies from scratch, turning a 2-minute build into a 15-minute build. Solutions include:
- GitHub Actions — Docker layer caching via the
docker/build-push-actionwithcache-fromandcache-totargeting the registry or GitHub Actions cache. - CircleCI — Docker Layer Caching (DLC) preserves layers across workflow runs on the same project.
- GitLab CI — Docker-in-Docker with cache mounts or using the kaniko builder with cache registry.
- Self-hosted runners — Persistent Docker daemon cache across builds on the same runner.
Container Registry Management
A container registry stores and serves Docker images. Every CI/CD pipeline that builds containers pushes to a registry and pulls from it during deployment. Registry selection, access control, and lifecycle management are critical infrastructure decisions.
Registry Options
Docker Hub is the default public registry but imposes rate limits (200 pulls per six hours for anonymous users, higher for authenticated). For production CI/CD, Docker Hub is suitable only for pulling public base images.
Cloud provider registries — Amazon ECR, Azure Container Registry (ACR), Google Container Registry (GCR) / Artifact Registry — provide tightly integrated, secure storage within the cloud ecosystem. ECR integrates with AWS IAM for fine-grained access control. ACR offers georeplication and vulnerability scanning. Artifact Registry supports both Docker and language-specific packages.
Self-hosted registries — Harbor (open-source, VMware-originated), Quay (Red Hat), and GitLab Container Registry (built into GitLab) — offer on-premises storage for compliance-sensitive environments. Harbor adds vulnerability scanning, image replication, and retention policies.
Registries integrated with CI/CD platforms — GitHub Container Registry (ghcr.io) is part of GitHub Packages and supports OCI containers with the same permission model as GitHub repositories. GitLab Container Registry is built into every GitLab project.
Image Tagging Strategies
Tag immutability is a security best practice. Once an image is pushed with a given digest, its tag should never be overwritten. This ensures deployments are reproducible and traceable to a specific commit.
Recommended tagging schemes:
- Git SHA tags —
registry.example.com/app:abc123def— every commit produces a unique, immutable tag. - Semantic version tags —
registry.example.com/app:1.2.3— used for releases. - Environment tags —
registry.example.com/app:staging-abc123def— combination of environment and commit.
Avoid latest tags in production pipelines. latest is mutable by definition and defeats the purpose of immutable deployments. If you must use latest for development convenience, ensure production deployments reference pinned tags or digests.
Kubernetes Deployments in CI/CD
Deploying containers to Kubernetes is the final stage of most container CI/CD pipelines. The deployment strategy controls how the new version replaces the old version with minimal downtime.
Rollout Strategies in CI/CD
Rolling updates, Kubernetes’ default strategy, gradually replaces old pods with new ones. The CI/CD pipeline triggers kubectl set image deployment/app app=registry.example.com/app:abc123def or applies a manifest with the new image tag. The deployment controller manages the transition with configurable surge and unavailable thresholds.
Blue-green deployments maintain two complete environments. The pipeline deploys the new version to the “green” environment, runs smoke tests, then switches the service selector to route traffic to green. The old “blue” environment remains ready for instant rollback.
Canary deployments route a small percentage of traffic to the new version. Tools like Argo Rollouts, Flagger, or Istio manage the canary lifecycle — gradually increasing traffic while monitoring error rates and latency. If the canary passes its evaluation period, traffic shifts 100% to the new version.
GitOps for Kubernetes CI/CD
GitOps uses a Git repository as the single source of truth for Kubernetes manifests. Tools like Argo CD and Flux continuously sync cluster state with the repository. The CI/CD pipeline builds the container image, updates the Kubernetes manifest in the Git repo with the new image tag, and Argo CD detects the change and deploys to the cluster. This pattern separates build CI from deploy CD and provides a clear audit trail of cluster changes.
Container Security in the Pipeline
Container security scanning must be integrated into the CI/CD pipeline before deployment. Scan for vulnerabilities in base images, application dependencies, and configuration.
Image scanning using Trivy, Snyk, or Grype checks the image’s operating system packages and application dependencies against vulnerability databases. Fail the pipeline if critical or high-severity vulnerabilities are found without a fix available.
Base image selection reduces the vulnerability surface. Alpine Linux images are smaller but use musl libc, which can cause compatibility issues. Distroless images (Google’s distroless/*) contain only the application and its runtime dependencies — no shell, no package manager, no unnecessary binaries. Chainguard Images provide minimal, actively maintained images with a CVE count near zero.
Singing and attestation using cosign (part of the Sigstore project) signs container images with cryptographic keys. Verification during deployment ensures the image was built by the authorized CI/CD pipeline and has not been tampered with since publication.
Frequently Asked Questions
Should I build Docker images inside a Docker container (Docker-in-Docker)?
Docker-in-Docker (DinD) is the traditional approach for CI environments but has security and performance drawbacks. Alternatives include mounting the Docker socket (not recommended for multi-tenant runners), using kaniko (Google’s container build tool that does not require Docker), or using buildah (part of Podman ecosystem). Most modern CI platforms support buildkit natively, which is faster and more secure than DinD.
How do I handle database migrations in container CI/CD?
Database migrations should run as an init container or job before the application deployment. In Kubernetes, use a Helm chart pre-upgrade hook or a separate migration Job in the CI/CD pipeline. Never run migrations as part of the application startup — race conditions between multiple replicas cause data corruption.
What is the ideal container image size?
Smaller images are faster to pull, store, and scan. Aim for under 200 MB for application images. Distroless and Alpine-based images typically range from 10 MB to 150 MB. Images over 1 GB indicate unnecessary dependencies, suboptimal layering, or missing multi-stage build patterns. Use docker dive or dive CLI to analyze layer content and identify size optimization opportunities.
How often should I rebuild base images?
Base images should be rebuilt at least weekly to pick up security patches. Use automated base image update tools like Dependabot (for Dockerfile FROM lines) or Renovate to create pull requests when a base image’s digest changes. Sign and scan rebuilt base images before republishing.
Recommended Internal Links
- CI/CD Guide: Continuous Integration and Deployment Explained — foundational pipeline concepts for container builds
- Deployment Strategies: Blue-Green, Canary, Rolling, and Feature Flags — detailed deployment patterns for containerized applications
- Security in CI/CD: Secrets Management, SAST, and Supply Chain Security — container image scanning and signing
Conclusion
Container CI/CD pipelines automate the journey from source code to running containers in production. Multi-stage Dockerfiles produce small, secure images. Registry management ensures images are stored safely and versioned immutably. Kubernetes deployment strategies — rolling, blue-green, canary — provide controlled rollouts with rapid rollback. Security scanning and image signing at each stage protect the software supply chain. Together, these practices enable teams to deliver containerized applications rapidly and reliably.
For a comprehensive overview, read our article on Artifact Management.
For a comprehensive overview, read our article on Ci Cd Best Practices.