Registry and Release Workflow

A registry is not just a place to push images. It is part of the release system. A good workflow makes every deployed image traceable, immutable, scannable, and easy to roll back.

Name images consistently

Use a stable repository name and clear tags.

1registry.example.com/team/api:<git-sha>
2registry.example.com/team/api:1.4.2
3registry.example.com/team/api:main
4registry.example.com/team/api:latest

Use immutable tags for release records.

  • Commit SHA tags are good deployment records.

  • Semantic version tags are good human release records.

  • Branch tags are convenient for development environments.

  • latest is only a moving convenience pointer.

Tag and push

Build and push with Buildx.

1export IMAGE=registry.example.com/team/api
2export GIT_SHA="$(git rev-parse --short HEAD)"
3
4docker buildx build \
5    --platform linux/amd64,linux/arm64 \
6    -t "${IMAGE}:${GIT_SHA}" \
7    -t "${IMAGE}:latest" \
8    --push .

Record the digest

Deployments should record the digest, not just the tag.

1docker buildx imagetools inspect "${IMAGE}:${GIT_SHA}"

The digest form is the immutable artifact reference.

1registry.example.com/team/api@sha256:<digest>

Promotion

Promote the same image between environments. Do not rebuild separately for staging and production unless the build inputs are intentionally different.

1build once -> scan -> deploy to dev -> deploy same digest to staging -> deploy same digest to production

Promotion can be implemented by updating deployment manifests to point at the same digest or by copying the image into environment-specific repositories.

Cache tags

BuildKit registry cache tags are not release tags. Keep them separate and expire them aggressively.

1docker buildx build \
2    --cache-from type=registry,ref="${IMAGE}:buildcache" \
3    --cache-to type=registry,ref="${IMAGE}:buildcache",mode=max \
4    -t "${IMAGE}:${GIT_SHA}" \
5    --push .

Registry policy

Use registry controls that match the risk of the environment.

  • Authenticate every push.

  • Restrict push access to CI or release automation.

  • Make production tags immutable.

  • Scan images on push and rescan when vulnerability data changes.

  • Retain release tags and digests longer than branch and cache tags.

  • Separate development and production repositories or accounts when access boundaries matter.

  • Keep SBOM and provenance attestations with release images.

Rollback

Rollback by digest when possible.

1registry.example.com/team/api@sha256:<previous-digest>

This avoids the ambiguity of a mutable tag. The deployment history should answer these questions quickly:

  • Which digest is running?

  • Which source commit created that digest?

  • Which Dockerfile and build target created it?

  • Which SBOM, scan report, and provenance attestation belong to it?

  • Which previous digest is known-good?

CI example

 1set -euo pipefail
 2
 3IMAGE="registry.example.com/team/api"
 4GIT_SHA="$(git rev-parse --short HEAD)"
 5
 6docker buildx build \
 7    --platform linux/amd64,linux/arm64 \
 8    --cache-from type=registry,ref="${IMAGE}:buildcache" \
 9    --cache-to type=registry,ref="${IMAGE}:buildcache",mode=max \
10    --sbom=true \
11    --provenance=true \
12    -t "${IMAGE}:${GIT_SHA}" \
13    --push .
14
15docker buildx imagetools inspect "${IMAGE}:${GIT_SHA}"

Practical rules

  • Build once and promote the same digest.

  • Treat latest as optional metadata, not the deployment source of truth.

  • Keep cache tags separate from release tags.

  • Use immutable release tags.

  • Store release metadata with the deployment.

  • Prefer digest references for production rollback.

References