ACR sync and image promotion — for when Git is someone else’s religion
Not every team falls in love with the same branching strategy, and not every pipeline starts from a tidy monorepo. Sometimes the artefact you trust is already a container digest in a registry, and the job is to move that digest to where production pulls from — with signatures, retention, and RBAC that make security nod instead of wince.
This post is about Azure Container Registry as the handshake between “we built something” and “the cluster may pull it,” especially when your developers would rather push an image than resolve a three-way merge. The punchline is not anti-Git — manifests in git should still point at immutable digests. The punchline is that registry-level sync is a first-class integration pattern, not a hack you apologize for in architecture reviews.
The cast of characters
| Idea | Role |
|---|---|
| Build registry | Fast iteration, noisy tags, CI writers |
| Prod (or shared) registry | Slower promotions, stricter RBAC, scanners |
| Digest | The real ID; tags are human-friendly stickers |
| Import / task | How you copy between registries without a laptop in the loop |
Think of promotion as teleporting a layer manifest, not rebuilding “the same Dockerfile somewhere else.” Same bytes, fewer arguments.
One-shot import: the hammer
az acr import is the CLI-shaped version of “copy this manifest into my registry.” It is ideal for pipelines that already know the source reference.
# Promote a build from the CI registry into the shared prod registry
az acr import \
--name prodregistry \
--source acrbuild.azurecr.io/platform/api:2026.04.15-abc123 \
--image platform/api:2026.04.15-abc123 \
--registry acrbuild.azurecr.ioIf the source is another subscription, you lean on token/password or managed identity with az acr login against both ends in the job — never embed long-lived passwords in YAML named secrets-final-FINAL.yml.
Tasks: when you want cron, not courage
ACR tasks are useful when sync is periodic — base image patching, mirror upstream library/python, or “every night, refresh our approved golden images.” A trimmed task definition:
# acr-task-mirror.yaml — illustrative
version: v1.1.0
steps:
- build: >-
-t {{.Run.Registry}}/cache/python:3.12-slim
https://github.com/your-org/dockerfiles.git#main:python-slim
timeout: 1800
- push:
- "{{.Run.Registry}}/cache/python:3.12-slim"Pair that with a trigger (timer or git) so nobody has to remember to “sync the base images” during an incident.
Promotion pipeline sketch (Azure DevOps flavour)
In CI, the artefact is already built. The promotion stage only needs identity, source ref, and target repository name:
# azure-pipelines-promote.yml — skeleton
stages:
- stage: PromoteImage
jobs:
- job: import_digest
pool: { vmImage: ubuntu-latest }
steps:
- task: AzureCLI@2
inputs:
azureSubscription: $(SERVICE_CONNECTION)
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail
SRC="${BUILD_REGISTRY}.azurecr.io/$(IMAGE_REPO):$(Build.BuildId)"
az acr import \
--name $(PROD_REGISTRY) \
--source "$SRC" \
--image $(IMAGE_REPO):$(Build.BuildId)Helm or Kustomize in git still updates image: prodregistry.azurecr.io/app@sha256:… — Git remains the intent; the registry is the proof the bytes arrived.
Retention: where money and drama meet
Untagged manifests and forgotten nightly builds are how registries turn into expensive attic storage. A policy-shaped approach beats manual spring cleaning (Premium SKU; applies to untagged manifests — read the docs before you surprise someone who pulls by digest):
az acr config retention update \
--registry myregistry \
--status enabled \
--days 30 \
--type UntaggedManifestsCombine that with soft delete where your compliance people allow it, so “oops” is recoverable without pretending disk is infinite.
The cultural footnote
Someone will always prefer docker push to git rebase. Give them a rail: import rules, locked-down service principals, and manifests that reference digests. You are not lowering the bar — you are meeting delivery where it lives and still enforcing immutability at the cluster edge.
If you only remember one line from this post, remember this: sync the digest, pin the manifest, automate the boring parts. Everything else is commentary.