Back to path
IntermediateBeacon · Project 5 of 12 ~8h· 6 milestones

Build a multi-environment delivery pipeline

Continues from the last build: You have immutable per-commit artifacts, but a deploy is still a single one-shot apply to one environment with no path to production.

Last rung gave it immutable images tagged by git short SHA, so any commit is now a reusable, reproducible artifact.

Designing an ordered CD promotion pipeline in GitHub ActionsReusing one immutable artifact across multiple environmentsModeling environments with GitHub Environments and required reviewersLayering per-environment Helm values without re-authoring the chartWriting post-deploy smoke checks that gate promotionTargeting a self-hosted runner so jobs can reach private cluster DNSPublishing environment URLs for traceability

What you'll build

You will have a single GitHub Actions delivery pipeline that takes one image:sha and promotes it through dev, staging, and prod as ordered, dependent stages running on the platform team's in-network self-hosted runner. Each stage deploys the existing Helm release with a per-environment values file, publishes its environment URL, and runs a post-deploy smoke check that fails the stage if the API is unhealthy. Production is protected by a GitHub Environment with required reviewers, so the same artifact that passed staging only reaches prod after an explicit human approval, giving it a real, auditable path to production.

See how we teach, before you sign up

You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:

.github/workflows/deliver.ymlyaml
name: deliver

on:
  push:
    branches: [main]

env:
  IMAGE: registry.internal/beacon
  SHA: ${{ github.sha }}

jobs:
  deploy:
    runs-on: [self-hosted, deploy-runner]
    steps:
      - uses: actions/checkout@v4
      - name: configure kube access
        run: echo "$KUBECONFIG_DATA" > kubeconfig
        env:
          KUBECONFIG_DATA: ${{ secrets.KUBECONFIG_DEV }}
      - name: deploy with helm
        run: |
          helm upgrade --install beacon deploy/chart \
            --set image.tag=${SHORT_SHA} \
            --values deploy/values-dev.yaml
        env:
          SHORT_SHA: ${{ github.sha }}

Reading this file

  • on: push: branches: [main]Every merge to main triggers delivery. You keep this trigger but fan it into a promotion chain.
  • runs-on: [self-hosted, deploy-runner]The job runs on the platform team's in-network runner; GitHub-hosted runners cannot resolve *.beacon.internal.
  • --set image.tag=${SHORT_SHA}image.tag is the promotion handle. The same SHA flows through every stage unchanged.
  • --values deploy/values-dev.yamlHard-codes dev. Per-environment values is exactly what the pipeline must parameterize.

The starting point: one job that deploys a single environment with copy-pasted intent. Note runs-on is the platform team's self-hosted runner label, because the cluster and the *.beacon.internal hostnames are only reachable from inside the private network. You will replace this with an ordered dev -> staging -> prod pipeline.

That's 1 of 9 explained code blocks in this single project.

The build, milestone by milestone

  1. 1

    Define the three environments as config, not copy-paste

    4 guided steps

    Promotion only works if dev, staging, and prod differ by configuration alone. The moment an environment needs a different chart or a different image, you no longer have a promotion pipeline, you have three separate deploys drifting apart.

  2. 2

    Add the production overlay as its own file

    4 guided steps

    Each overlay must be its own file so a single helm upgrade can be pointed at exactly one of them with --values. Jamming two overlays into one file with a YAML document marker produces a malformed multi-document file that helm will not accept as a values file.

  3. 3

    Split the one-shot deploy into a reusable per-environment job

    4 guided steps

    If each environment is hand-written, the dev deploy and the prod deploy slowly diverge and a fix applied to one never reaches the other. One parameterized deploy-and-smoke unit guarantees every environment is shipped the identical way.

  4. 4

    Chain dev -> staging -> prod as ordered, dependent stages

    4 guided steps

    Ordering is what makes it a promotion pipeline rather than three parallel deploys. needs encodes the rule that you cannot reach a later environment until every earlier one is healthy, which is the whole safety guarantee of staged delivery.

  5. 5

    Gate production behind a GitHub Environment with required reviewers

    4 guided steps

    Automated promotion is powerful and dangerous; the last hop to prod should require a deliberate human decision with an audit trail. GitHub Environments give you that gate, the reviewer record, and the environment URL in one place, without writing custom approval logic.

  6. 6

    Make every stage report its URL and prove the same SHA shipped end to end

    4 guided steps

    A delivery pipeline is also a record. When the on-call asks which SHA is live, the answer should come from the pipeline's own output, not from someone's memory. Surfacing the URL and SHA per stage turns the pipeline into the source of truth.

What's inside when you start

3 starter files, ready to clone
6 guided milestones
6 full reference solutions
9 code blocks explained line-by-line
6 "is it working?" checks
4 interview questions it prepares you for

You'll walk away with

A single deliver.yml GitHub Actions pipeline with ordered deploy-dev, deploy-staging, and deploy-prod jobs chained by needs, all running on the in-network self-hosted runner and promoting one image:sha
Three per-environment Helm values files, each its own single-document file with identical keys at every level, no pinned image tag, and no secrets
A reusable scripts/smoke.sh that fails its stage if the environment /healthz does not pass within a bounded wait
A GitHub Environment named prod with required reviewers and environment-scoped kubeconfig secrets that pauses the pipeline before production
Per-stage step summaries showing each environment URL and the deployed short SHA, proving the same artifact shipped end to end

This is portfolio-grade. Build it free.

Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.

Start building