Skip to content

Phase 2 · CONTAINERS & CI/CD

Phase 2 review + Broken Pipeline

Day 40 of 90 ~45 min 0/25 in phase Builds on Day 39

By the end of today

  • Recap the Containers & CI/CD arc from Docker to secure releases
  • Trace a red release pipeline to a rotated secret and fix it
  • Answer Phase-2 CI/CD interview questions with calm, layered reasoning

The Phase 2 arc: from a Dockerfile to a pipeline you trust

Section 1 of 5 · ~2 min

Five weeks ago an app only ran the way your laptop happened to be set up. Phase 2 cut that cord twice. First you packaged it: an image is the read-only recipe, a container a running instance, and a Dockerfile the thing that builds it (days 22–24). You wired containers together on a network, gave their data a volume that outlives a docker rm, and described a whole multi-container stack in one compose.yaml (days 25–28). Then you made those images lean and safe with multi-stage builds, and learned to debug a live one with logs, exec and inspect (days 29–30).

The second cut was CI/CD. A single YAML file under .github/workflows/ turned push code into a fresh runner builds, tests and ships it (days 31–34). Then you added the rest of a real pipeline: automated tests and quality gates so a red build blocks a merge (day 36); semantic versioning and tags so every release has a name you can reason about (day 37); deploy strategies — rolling, blue-green, canary — so shipping doesn’t mean downtime (day 38); and pipeline security — secrets kept out of the repo and images scanned with Trivy before they ship (day 39).

Stack those and you get the shape every containerized delivery pipeline shares: build → test → scan → tag → publish → deploy. Each stage hands the next an artifact it has to trust — and the whole line runs itself on every push.

The Phase 2 delivery pipeline: build the image, run tests and a Trivy scan, tag it with the commit SHA and a semver tag, publish it to a registry, then deploy — the publish step is the one that needs a registry credential, and it's where today's Broken Pipeline mission fails. build image test + scan gates · Trivy tag SHA · semver publish needs credential deploy rolling · b/g the publish leg is where the mission's pipeline fails — a rotated secret
The build → test → scan → tag → publish → deploy line every commit walks — and the publish step, where a missing credential stops an otherwise-green run.

Real world: A delivery pipeline is a relay team. Each runner — build, test, scan, publish, deploy — has to pass the baton cleanly to the next, and every runner can be perfectly fit, yet if one arrives at the exchange without the wristband that proves they’re allowed on the track, the handoff is refused and the whole team is stopped. That wristband is a credential. When today’s mission pipeline dies on the publish leg, no runner is injured — one just lost the pass that lets it hand the baton to the registry.

GitHub Actions made this concrete across week 5: the same workflow that builds your image logs in to a registry with a token and pushes the exact SHA-tagged image the run produced. That login step is the quiet load-bearing part — everything upstream can be green while the pipeline still fails the moment the credential it presents is missing, expired, or rotated out from under it.

Which is exactly today’s mission. Reading about a red pipeline doesn’t build the instinct; tracing one does. Broken Pipeline hands you a release that went red on a Friday afternoon — build green, tests green, then unauthorized: authentication required on the publish job — and the week-5 knowledge to follow the failure to a rotated secret nobody re-added, without rewriting the workflow.

Hands-On Lab

Section 2 of 5 · ~2 min

Today the lab is the mission, and it’s the Phase-2 boss fight. No WSL2, no setup, nothing to paste — Broken Pipeline runs entirely in your browser.

A teammate merged a release on Friday afternoon and left. The pipeline that had been green for weeks is suddenly red, and the deploy never happened. The code compiles, the tests pass, the image builds — the run only dies on the last leg, publishing to the registry, with unauthorized: authentication required. Nothing about the app changed. Play it as the walk down the pipeline diagram, stopping at the first leg that’s broken:

  • Read the run, top to bottom — the Actions log shows build and test green and the failure isolated to the publish job. A failure that only appears at publish, after everything else passed, is telling you the problem is authentication, not code (days 32–34).
  • Read the actual errorunauthorized: authentication required (a 401) from the login/push step means the credential the workflow presented was empty or rejected. This is not a broken Dockerfile and not a flaky runner (day 34’s GITHUB_TOKEN and registry login).
  • Find the missing secret — the workflow references a repository secret, REGISTRY_TOKEN, to log in. Someone rotated that token for security and never added the new value back, so the secret name still resolves but its value is gone — login sends nothing (day 39’s secrets management).
  • Restore it, don’t rewrite it — the fix isn’t editing the workflow. It’s re-adding the secret with the current token: gh secret set REGISTRY_TOKEN. Set it, re-run the failed job, and watch publish and deploy go green.

Type help in the terminal to see the supported commands, and hint if you stall — it nudges without solving. There’s no penalty for exploring; the whole point is to run the read the run → read the error → find the secret → restore it walk with your hands until the reflex sticks, and to feel why a perfectly correct workflow still fails the instant the credential behind a ${{ secrets.NAME }} reference goes missing.

When the pipeline finally goes green end to end, come back and note below which leg you suspected first — that reflection is where the lesson sets.

Common Errors & Fixes

Section 3 of 5 · ~3 min

These are the failures people actually hit around release pipelines on GitHub Actions — the same links the mission rehearses. Read the error text slowly; parsing it is the skill.

Common error: A publish job failing to authenticate to the registry because the secret it logs in with was rotated and never re-added:

Error: unauthorized: authentication required
ERROR: failed to push ghcr.io/acme/store-api:1.4.2: failed to authorize: failed to fetch oauth token: unexpected status from GET request: 401 Unauthorized
Error: Process completed with exit code 1.

Why: The workflow’s login step reads a repository secret — REGISTRY_TOKEN — for its password. If that token was rotated and the new value never added back, the secret name still resolves but expands to an empty string, so login presents no real credential and the registry answers 401 / authentication required. Build and test are untouched; only the authenticated push fails.

Fix: Re-add the secret with the current token value — gh secret set REGISTRY_TOKEN (paste the new token when prompted, or pipe it in), or add it under Settings → Secrets and variables → Actions — then re-run the failed job. Confirm the new token still carries push/write scope for that registry.

How you’d spot it in prod: A pipeline that was green for weeks and suddenly fails only on the push/publish step with 401 or authentication required, while build and test stay green, almost always means a credential expired or was rotated — not a code change. Check the secret’s age against the failure time before you touch the workflow.

Common error: A login step failing because the workflow references a secret whose name doesn’t match the one actually stored:

Error: Username and password required
Error: Process completed with exit code 1.

Why: Secret names are case-sensitive and exact, and docker/login-action refuses to run unless it has both a username and a password. This login pulls both from secrets — username: ${{ secrets.REGISTRY_USER }}, password: ${{ secrets.REGISTRY_TOKEN }}. If either name is mistyped — say the workflow says ${{ secrets.REGISTRY_USR }} but the stored secret is REGISTRY_USER — that reference resolves to an empty string, the action sees a missing field, and it errors before it ever reaches the registry. An environment-scoped secret that the job never declares an environment: for empties out the same way.

Fix: Make the names match exactly. List what’s actually stored with gh secret list and compare it, character for character, against the ${{ secrets.X }} names in the workflow. If the secret lives in an environment, add the matching environment: key to the job so the run can see it.

How you’d spot it in prod: A brand-new or just-edited workflow that fails at the login step (not the push) with a “username and password required” style message usually means the secret name doesn’t resolve — a typo or a wrong scope, not an expired credential. Diff the referenced name against gh secret list.

Common error: A release blocked by the image-scan gate after a base-image or dependency bump pulled in a vulnerable package:

store-api:1.4.2 (debian 12.5)
============================
Total: 3 (HIGH: 2, CRITICAL: 1)

Error: Process completed with exit code 1.

Why: The pipeline runs Trivy as a quality gate with --exit-code 1 --severity HIGH,CRITICAL, so any HIGH or CRITICAL finding makes the scan step exit non-zero and fails the build on purpose. A newly disclosed CVE in the base image or a bumped dependency trips it. That’s the gate doing its job, not a broken scanner.

Fix: Read the CVEs it lists, then bump the base image or the flagged package to a patched version and rebuild. If a finding is a confirmed false positive or genuinely has no fix yet, record it in a .trivyignore with the specific CVE id and a note — never delete the gate to make the run green.

How you’d spot it in prod: A release that passed last week failing on the scan step this week, right after a base-image or dependency change, points to a fresh CVE in what you pulled — the diff is the new package, not your code. Read the Trivy summary before assuming the pipeline is broken.

CI/CD Pipeline Interview Questions

Section 4 of 5 · ~1 min

These four are what a screening round asks once Phase 2 is behind you — they span the whole build-test-ship arc and the debugging instinct the mission drills. A calm, layered answer that explains why a green build can still fail to ship beats reciting YAML keys. Cover each answer, say your own version out loud first, then compare — recalling before revealing is what makes it stick for interview day. The four questions and answers render right after this note.

Go Deeper

Section 5 of 5 · ~1 min

Optional extras if you have ~30 more minutes today:

  • 5 min — Replay Broken Pipeline and try to name the failing leg from the run log alone, before reading the error — speed here is just knowing the build → test → scan → publish → deploy walk cold.
  • 10 min — On a throwaway repo with a working push-to-registry workflow, delete or rename the secret it logs in with, push, and watch the publish step fail with authentication required; then gh secret set the value back and re-run to watch it recover. Feel the exact gap the mission exploits.
  • 15 min — Read GitHub’s Using secrets in GitHub Actions — repository vs environment secrets, masking, and rotation — the reference for every credential your Phase-2 pipelines depend on.
Walk me through what happens between a git push and a running container in production. Both

On push, GitHub matches the event to a workflow under .github/workflows and spins up a fresh runner. It checks out the code, runs the tests and any quality gates, and — if they pass — builds a Docker image from the Dockerfile. It tags that image with the commit SHA, logs in to a registry, and pushes it. A deploy step then pulls that exact tagged image and rolls it out, usually rolling or blue-green so there's no downtime. The thread running through all of it is traceability: the image in prod maps back to one commit, one test run and one scan, so I know exactly what shipped and can roll back to that tag.

A release pipeline that worked yesterday fails on the publish step with 'unauthorized: authentication required'. How do you debug it? Both

First I read where it fails. Build and test are green and only the push to the registry dies, so it's a credentials problem, not a code one. 'Unauthorized: authentication required' means the login step handed the registry an empty or invalid token. My first check is the secret the workflow references — was it rotated, renamed, or never added to this repo? A registry token that was rotated and never re-added is the classic cause: the workflow still names the secret, but its value is gone, so login sends nothing and the registry rejects the push. The fix is to set the secret again — gh secret set REGISTRY_TOKEN — and re-run. I'd also confirm the new token still has push scope.

How do you keep secrets out of your pipeline and your images? Both

Secrets never go in the repo or the Dockerfile — anything committed is effectively public and lives forever in git history. In GitHub Actions I store them as encrypted repository or environment secrets and reference them as ${{ secrets.NAME }}; the runner injects them at run time and masks them in logs. For registry pushes I prefer the built-in GITHUB_TOKEN — short-lived and scoped to one run — over a long-lived personal token. Build-time secrets use --secret mounts, not build args, so they don't bake into a layer. And I grant least privilege: packages: write and nothing more, so a leaked token can do as little as possible.

Explain rolling, blue-green and canary deploys. Product

All three replace an old version with a new one without a hard cutover. A rolling deploy swaps instances a few at a time, so old and new run side by side until every instance is updated — simple, but a bad version reaches everyone gradually. Blue-green keeps two full environments: you deploy to the idle one, test it, then flip all traffic at once, which makes rollback instant — you just flip back. Canary sends a small slice of traffic, say 5%, to the new version, watches the metrics, and ramps up only if it stays healthy, so a bad release hits few users. I choose based on the risk of the change and how fast I need to roll back.

Mark Day 40 complete

Tomorrow you start Project 1 — five days to scope, containerize, compose and ship a real multi-service app with its own CI pipeline.

Mission unlocked: Broken Pipeline — you have the skills now.

Play (15–20 min)

Stuck on today’s lab? Ask in Mission 90 Q&A