4.5 · CI: building and pushing an image on every commit

Level 4 · Registries, reliability & CI

4.5CI: building and pushing an image on every commit

Goal: Automate build + push of a versioned image with GitHub Actions.
Estimated time: 13 min · self-paced reading · If you only have 5 min: read “The essentials in 30s” + the diagram.

The essentials in 30 seconds

  • CI is the only image factory: on each push to main, it fetches the code, logs in to the registry and builds + pushes — no more “hand-built on the laptop” builds.
  • Tag SHA + version: the commit fingerprint for exact traceability, the version for readability — production deploys the SHA or the version, never a moving alias.
  • Idir's reflex: cache enabled for fast builds, credentials in repository secrets, never in the workflow file.

Threaded mini-case — Idir plugs in his team's image factory

Idir, junior DevOps in Algiers, is tired of hand-built images whose content nobody knows: he writes a workflow that, on each push to main, logs in to GHCR via the repository token, builds with cache and pushes the image tagged with the commit fingerprint. The team then pulls the image of the exact commit. Last milestone of the thread: each commit yields a deployable, traceable image, with no manual intervention.

Last case of thread 4.1-4.5 — Idir completes his level: publish, harden, expose, back up, automate.

1. The minimal pipeline that changes everything

On each push to main: fetch the code, log in to the registry (repository secret), build + push tagged. No more “hand-built on the laptop” builds: the CI is the only factory for images — like Idir's factory, each image has a known origin.

And only ship green: a test job before the build avoids pushing broken images.

2. Tagging smartly in CI

Push the commit fingerprint (exact traceability: this image = this code) + the version (readable, on git tag) + a branch alias in development. In production deploy the fingerprint or the version, never a moving alias — Idir can thus redeploy exactly the validated commit.

Pushing only the moving alias means becoming untraceable again: the pipeline must always produce at least one exact tag.

3. Cache and secrets: the two settings

Cache reuses layers between runs and divides build times. Credentials live in repository secrets, never in the workflow file. A pipeline showing a secret in logs is a pipeline to fix urgently — then revoke the exposed secret.

Minimum permission: the job only gets the rights it needs (package writes, nothing more).

Common misinterpretation: “Building locally then quickly pushing, tagging only the moving alias and putting the token in the file: it's faster”. No. Local builds are irreproducible, the moving alias alone is untraceable in production, and the plain-text token in the file leaks from the first share. CI as the only factory, systematic exact tag, secrets out of the file: this trio is what makes a reliable factory.
The image factory Commit, cached build, tagged push Push to main Teststhen buildRegistry login RepositorysecretBuild + cache ReusedlayersExact tagged push Fingerprint+ version
Commit, cached build, tagged push
Text version of the diagram (SEO / screen readers / translation): 1) Idir writes a workflow triggered on each push to main, with a test job before the build. 2) The workflow logs in to GHCR via the token stored as a repository secret, with minimal permissions. 3) It builds with cache and pushes the image tagged with the commit fingerprint (plus the version on git tag). 4) The team deploys the exact fingerprint, never the moving alias. Lesson: CI builds and pushes, the exact tag traces, secrets stay out of the file.

🔎 Concrete example — 🔎 Concrete example — Minimal GitHub Actions workflow

Idir's factory: login via repository token, build + push of the fingerprint.

name: image
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          push: true
          tags: ghcr.io/monequipe/app:${{ github.sha }}

Reading: login via automatic token, build + push of the commit fingerprint.

Lesson: each commit yields a deployable, traceable image.

Section vocabulary

Pipeline
The automatic CHAIN: test, then build, then push (then deploy).

Simple example: On each push, Idir's factory tests, builds and pushes with no intervention.

What it is not: commands typed by hand on a laptop.

Reflex: repeating a build by hand: write it as a pipeline.

SHA
The commit FINGERPRINT: exact-traceability tag (this image = this code).

Simple example: Idir redeploys exactly the image of the validated commit.

What it is not: a moving alias changing content.

Reflex: production: deploy a fingerprint or a version, never an alias.

CI secret
SENSITIVE variable stored in the repository, masked from logs and never written in the file.

Simple example: Idir's registry token lives as a secret, not in the workflow.

What it is not: a password pasted in plain text in the file.

Reflex: credentials in CI: repository secret, always.

Single factory
CI ALONE produces deployed images: known origin, reproducible builds.

Simple example: Idir's team only deploys images coming out of the pipeline.

What it is not: images pushed “quickly” from a laptop.

Reflex: image of unknown origin: forbid, go through CI.

In practice — In practice — Your first factory

  1. Create the above workflow in .github/workflows/image.yml.
  2. Push to main and follow the run (green?).
  3. Check the tagged image in the registry packages.
  4. Pull it and run it: the commit runs.
Each commit produces a traceable image, with no manual intervention.

Ready-to-say script — announcing the factory to the team (30 s)

“This is Idir: on each push to main, the CI tests, builds with cache and pushes the image tagged with the commit fingerprint. The token stays as a repository secret, never in the file. We deploy the exact fingerprint: no more images whose content nobody knows.”

Say in review: show the green run and the commit's image in the registry.

Copy-paste template — build + push workflow (generic, valid)

name: image
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/monequipe/app:${{ github.sha }}

Readable version on top

Like Idir: commit fingerprint + version on git tag — exact traceability and human reading.

Builds too slow

Enable cache between runs: unchanged layers are reused, builds are divided.

Broken image pushed

Add the test job before the build: only ship green.

Secret shown in log

Fix urgently, remove any debug output, then revoke and replace the exposed secret.

Caution: Registry tokens in CI are secrets: store them in repository secrets, never hard-coded in the workflow — and never show them in logs. Give the job minimal permissions (package writes only) and revoke any exposed secret before continuing.

✓ Best practices

  • Systematic exact tag + version on git tag.
  • Enable build cache.
  • Secrets in repository secrets, never in plain text.

✗ Common pitfalls

  • Building locally then pushing “quickly”.
  • Pushing only the moving alias (untraceable).
  • Showing a secret in debug echo.

Key takeaways

  • CI builds + pushes, not the laptop.
  • Fingerprint = traceability, version = readability.
  • Cache = fast builds.
  • Secrets out of workflow, always.

48h challenge — apply tomorrow

On a real repository: create the workflow, push to main, check the tagged image in the registry then run it.

Section exit checklist
  • I can write a build + push workflow triggered on main.
  • I can tag fingerprint + version and deploy the exact.
  • I can manage secrets, cache and minimal permissions.
Self-rating 1-5: could I say which commit produced the production image?does my workflow expose a secret anywhere?

Frequently asked questions

Should you test before building?

Yes: a test job before the build avoids pushing broken images. Only ship green.

And auto-deployment?

Next step: first a reliable factory (traceable build + push), then delivery. Without a reliable factory, automating deployment only accelerates mistakes.

Check your understanding

In CI, which tag do you deploy to production?

Additional resources (optional, to go further)

Level 4 examFresh, harder questions to test what you've learned