CI/CD·7 min read·

Immutable Artifacts: Build Once, Promote Across Environments

Stop rebuilding in every environment. Build a versioned artifact once in CI and promote it through staging to production unchanged.

NB

Netbay Developer Relations

Netbay Engineering

On this page

A surprising number of deployment pipelines rebuild the application on every target: CI builds once, then the staging server runs another build, then production runs yet another. If any step produces different output — a non-deterministic dependency resolution, a differing base image, a changed timestamp — you are testing and shipping different software without realizing it. Immutable artifacts fix this: build exactly once, and promote that single, versioned build from stage to stage without touching it.

What immutable means in practice

An immutable artifact has three properties:

  • Built once, producing a byte-identical output that is stored and referenced by version.
  • Never modified after creation; any change produces a new artifact with a new version.
  • Referenced only by that version (a tag or digest), so you can always inspect exactly what is running.

For containerized apps, a container image with a deterministic content digest is the ideal vehicle. You push the image once, record its digest, and every environment — staging, canary, production — deploys from that exact digest. Re-tagging in place, or rebuilding at deploy time, breaks the contract.

bash
# build once
docker build -t registry.example.com/web:$(git describe --tags) .
docker push registry.example.com/web:$(git describe --tags)
# record the digest for promotion
docker inspect --format='{{index .RepoDigests 0}}'   registry.example.com/web:$(git describe --tags) > image.digest

The digest line you store is the source of truth for what runs next, letting every later environment reference the exact bytes that were tested.

The promotion agenda in CI

Promotion is the act of taking a tested artifact and deciding it is allowed in the next environment. Modeling this in CI keeps it explicit and auditable. A typical promotion flow runs the artifact through increasingly strict gates — unit tests, then integration, then a staging deploy, then production.

yaml
name: promote
on:
  workflow_dispatch:
    inputs:
      environment:
        description: promotion target
        required: true
        type: choice
        options: [staging, production]
      image:
        description: artifact digest to promote
        required: true
jobs:
  promote:
    runs-on: ubuntu-latest
    steps:
      - name: deploy pinned digest
        run: |
          docker pull app:latest
          docker tag app:latest app:current
          docker push app:current

Every promotion references the same artifact from staging all the way to production, so the exact bytes tested in staging are the bytes released to users. (Pinning by digest in your real registry is even stricter than the tag shown here.)

build artifact v1.0 digest + tag immutable staging canary production same artifact promoted, never rebuilt rollback targets any previous tagged version

Why rebuilding is a hidden risk

When each environment rebuilds, two problems appear. First, you cannot reproduce a bug in production by running the staged build, because they differ. Second, rollbacks become impossible with confidence — the old version may no longer build, or may build differently today. With immutable artifacts, rollback is trivial: point the environment back at a previously recorded digest that you know worked.

Versioning and metadata that help audit

Give artifacts rich, machine-readable identity. Embed the Git SHA, commit timestamp, and build number in the image labels so any running instance can tell you its provenance. This makes debugging substantially faster, because the running image declares what produced it.

dockerfile
FROM node:20-slim
ARG REVISION=dev
ARG CREATED=unknown
LABEL org.opencontainers.image.source="https://github.com/acme/web"
LABEL org.opencontainers.image.revision="$REVISION"
LABEL org.opencontainers.image.created="$CREATED"

CI generates the revision and created timestamps as build arguments and passes them in, so provenance is baked into the image at build time rather than guessed.

Deterministic builds and the last mile

Truly immutable promotion assumes builds are reproducible enough that the artifact faithfully represents the source. Pin base images by digest, use lockfiles, and avoid pulling the latest tag at build time. The final mile is configuration: the immutable artifact should still be deployed with environment-specific config injected at runtime, so the artifact itself remains unchanged while its settings differ by stage.

Takeaway: build once, capture the digest, and promote that identical artifact through every environment. You get tested-and-shipped sameness, instant rollback, and clean audits. Running that pipeline against a Netbay VPS in Lucknow DC01, with a new instance in under 60 seconds, keeps your promotions fast and trustworthy — netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance