Azure 40: Multi-Registry Docker Build & Push — the loop pattern
easy⏱ 5 mincourseazure
Build once, push many
docker build is expensive, especially when the build pulls Gradle / npm dependencies behind a corporate proxy. Lythia builds the image once on a single self-hosted agent, then loops over registryServiceConnections[] to push the SAME image hash to dev, test, uat ACRs (prod ACR receives a separate copy after promotion gates). Each push is a tag/retag, not a rebuild.
parameters:
- name: registryServiceConnections
type: object
default: []
- name: imageName
type: string
- name: tag
type: string
steps:
- $\{\{ each rsc in parameters.registryServiceConnections \}\}:
- task: Docker@2
inputs:
command: push
containerRegistry: $\{\{ rsc \}\}
repository: $\{\{ parameters.imageName \}\}
tags: $\{\{ parameters.tag \}\}
buildArgs the smart way
Build args you ALWAYS pass: ado_artifacts_token=$(System.AccessToken) for Gradle/Maven private feeds; REGISTRY_URL=$(acrContainerDev) so the runtime base image (e.g. gradle-jdk21) is pulled from the closest ACR; PROJECT=routes to switch between sub-modules in a multi-project Gradle build. Args that change between envs go through Helm --set, NOT through the Docker layer.
- task: Docker@2
inputs:
command: build
Dockerfile: '$(servicePath)/Dockerfile'
arguments: |
--build-arg ado_artifacts_token=$(System.AccessToken)
--build-arg REGISTRY_URL=$(acrContainerDev)
--build-arg PROJECT=routes
repository: $(serviceName)
tags: $(GIT_SEM_VER)
Build the registry fanout
Write a planFanout(env, imageName, tag) that returns the list of fully qualified image refs to push. Rules: dev → push to dev only; test → push to test only; uat → push to test AND uat (test ACR is the source of truth for promotion); prod → push to uat AND prod. Generate refs as <registry>/<imageName>:<tag> and <registry>/<imageName>:latest. Log the plan for each env.
ACR Tasks for cross-region replication
If you also serve from eastus or europe, don't push N times from the build agent — push once to a Premium SKU ACR with geo-replication and let Azure replicate. Premium also unlocks content trust (signed tags) which you should require for prod images.
Quiz: same digest across registries?
You push the same locally-built image to dev and uat ACRs. Will docker pull drivewayprodcontainerregistry.azurecr.io/cart-api@sha256:abc... and docker pull drivewayuatcontainerregistry.azurecr.io/cart-api@sha256:abc... return identical bits? Yes — the digest is content-addressable. That's why image promotion can simply re-tag instead of rebuilding.