Topic 8.2
Docker in a CI/CD Pipeline
In one line
The exact same docker build and docker push commands you've been running by hand become the backbone of an automated pipeline — the shift is WHO runs them and WHEN, not what they do.
Key ideas
- 01
A typical CI/CD flow for a containerized app: a developer pushes code → the CI system (GitHub Actions, GitLab CI, Jenkins) automatically checks it out → runs
docker buildto produce an image → runs the app's test suite (often INSIDE a container, for environment consistency) → if tests pass, tags and pushes the image to a private registry → a deployment step pulls that exact image and runs it in staging/production. - 02
CI runners themselves often run Docker (or use a Docker-in-Docker setup) specifically SO THAT they can execute
docker buildas part of the pipeline — meaning your CI environment is itself frequently just another machine with Docker installed, running the same commands you've been running manually throughout this course. - 03
The image built in CI should be the EXACT SAME image that eventually runs in production — never rebuild the image separately for each environment. Build ONCE, tag it clearly (often with the git commit SHA for perfect traceability), push it once, and PROMOTE that same immutable artifact through staging and production by just changing which environment pulls and runs it.
- 04
This 'build once, promote everywhere' principle directly reuses Topic 1.4's digest/tag immutability lesson — you want production running the LITERAL, byte-identical image that was tested in staging, not a fresh rebuild that happens to use the same source code (which could theoretically differ if a dependency's version resolution isn't perfectly pinned, or the build environment itself changed slightly).
- 05
Layer caching (Phase 2.2) becomes even more valuable in CI, where build time directly costs real money (compute minutes) and developer waiting time — many CI providers offer registry-backed or local build-cache options specifically to preserve Docker's layer cache BETWEEN separate CI runs, not just within one developer's local machine.
- 06
A CI pipeline is also the natural place to run the vulnerability scanning from Phase 7.2 automatically on every build, and to FAIL the pipeline if a critical vulnerability is found — turning a manual, easy-to-forget security check into an enforced, automatic gate nobody can accidentally skip.
In your stack
- →
A realistic GitHub Actions step for a Spring Boot project:
docker build -t myapp:${{ github.sha }} .followed bydocker push, using the git commit SHA as the tag — this gives every single build a unique, traceable identifier tying a running production container directly back to the exact commit that produced it.
Code & diagrams
The core build-tag-push-scan sequence as CI steps — the same commands you've typed by hand, now automated.
# .github/workflows/build.yml (excerpt)
steps:
- uses: actions/checkout@v4
- name: Build image, tagged with the commit SHA for full traceability
run: docker build -t myapp:${{ github.sha }} .
- name: Scan for known vulnerabilities — fail the pipeline on critical findings
run: docker scout cves myapp:${{ github.sha }} --exit-code --only-severity critical
- name: Log in to the private registry
run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login myregistry.example.com -u ci --password-stdin
- name: Tag for the registry and push
run: |
docker tag myapp:${{ github.sha }} myregistry.example.com/myapp:${{ github.sha }}
docker push myregistry.example.com/myapp:${{ github.sha }}
# Later stages (staging, then production) PULL and run this EXACT tag —
# nothing ever gets rebuilt separately per environment.Explain it without notes
Why is 'build once, promote the same image everywhere' considered a best practice over 'rebuild the image separately for each environment'?
Why does tagging a CI-built image with the git commit SHA matter more than just using a version number like v1.2.3 alone?
Practice
Sketch (on paper or in a text file) a CI pipeline for a project you have, listing each stage in order: checkout, build, test, scan, tag, push, deploy.
If you have access to any CI system (GitHub Actions is free for public repos), set up a minimal workflow that builds a Docker image on every push and confirm it runs successfully.
Trade-offs
- ↔
A fully automated build-scan-push pipeline takes real setup time and requires managing CI secrets (registry credentials) securely — a meaningful upfront investment that pays for itself almost immediately on any project with more than one contributor or more than one deployment.
Done when you can
I can sketch a complete CI/CD pipeline for a containerized app from memory.
I understand exactly why 'build once, promote everywhere' beats rebuilding per environment.
I know why commit-SHA tagging matters for traceability.