Why does a CI job push one container image under both a git-SHA tag and a `stable` tag?
answer
- Two names, two different jobs
- One name must never change meaning
- The other is re-pointed every release
- Redeploy needs a name nobody moved
- docker tag then a second push
basics
~20 sThe git-SHA tag is a permanent name for one exact build, so it can be redeployed later and traced back to a commit. The stable tag only points at whichever build is current, so it answers what is live now, not what shipped when.
solid answer
~50 sThe two tags do different jobs, and one tag cannot do both. A tag derived from the commit, such as `indexer:7e1c94d`, is written once and never re-pointed: it is a permanent, human-readable name for one specific set of bytes, so months later you can still start that exact build and know which commit produced it. An alias such as `indexer:stable` is deliberately mutable — each release re-points it with `docker tag indexer:7e1c94d indexer:stable` followed by a second `docker push`, which uploads only a manifest because the layers are already in the repository. Deployments and rollbacks should name the immutable tag; the alias is a convenience for humans, local runs and "give me something that works" pulls. If the alias is the only tag you ever push, every past release loses its name the moment the next one ships.
code
bash · 7 lines# build once, name it after the commit, push that name first
docker build -t registry.internal:5000/indexer:7e1c94d .
docker push registry.internal:5000/indexer:7e1c94d
# then move the alias as a separate, deliberate step
docker tag registry.internal:5000/indexer:7e1c94d registry.internal:5000/indexer:stable
docker push registry.internal:5000/indexer:stablego deeper
Be ready to say plainly what each of the two tags is for and to write the two commands that add and push an alias. Knowing that both names can point at one image ID is the core of it.
Explain the mechanics: docker tag adds a name with no copy, the second push writes only a manifest, and re-pointing an alias leaves the previous manifest intact but nameless unless its own build tag exists.
Show the operational consequence. An interviewer expects you to connect the convention to rollback: deployments name the immutable tag, aliases move afterwards, and no production action ever depends on a name someone else can re-point.
Own the rule rather than the commands. Decide which names in your registry are allowed to move, who may move them, and where the history of those moves is recorded, since the registry itself keeps none.
### Two jobs, two tags A container image reference like `registry.internal:5000/indexer:7e1c94d` is a name a registry resolves to a manifest. Names are cheap: the same image can carry as many of them as you like, in the same repository, and pushing a second name for an image already in the repository transfers almost nothing because the blobs are already there. That cheapness is what makes the two-tag convention work. A release pipeline is being asked for two different things at once, and they are in direct conflict: 1. **"Run exactly the build we tested."** This needs a name that will mean the same thing forever. If the name can be re-pointed, then "deploy `indexer:stable`" is not an instruction, it is a bet on what somebody else has done since. 2. **"Run whatever is current."** A developer pulling the service to run it locally, a smoke-test job, a `FROM` line in a downstream image — these want the newest good build without being told its identifier every time. That needs a name that *does* move. One tag cannot satisfy both. So the pipeline pushes two. ### What the build tag looks like The immutable tag is derived from something the build already knows and never reuses: the commit that was built (`7e1c94d`), the pipeline's build number (`4183`), a release version, or a combination such as `1.4.2-4183-7e1c94d`. The rule that matters is not which of those you pick — it is that **the pipeline writes that tag once and no later build ever writes it again**. It is the name you put in a deployment, quote in an incident channel, and roll back to. ### What the alias looks like `stable`, `prod`, `edge`, `nightly`, `main` — the exact word is convention. What defines an alias is that it is re-pointed on purpose. Mechanically that is two commands after the build has been pushed: ``` docker tag registry.internal:5000/indexer:7e1c94d registry.internal:5000/indexer:stable docker push registry.internal:5000/indexer:stable ``` Locally, `docker image ls` now shows two rows with the *same* IMAGE ID — `docker tag` did not copy anything, it added a name. In the registry, the second push finds every layer already present ("Layer already exists") and writes only the manifest under the new tag. The previous build that used to be behind `stable` has not been deleted or damaged; it simply is not called `stable` any more. It is still reachable by its own build tag — which is exactly why that build tag has to exist. ### The failure this convention prevents The interesting question is what happens when a team pushes only the alias. Every release overwrites the meaning of the one name that exists. There is then no way to say "run the build from before lunch", because that build has no name at all. Rolling back becomes rebuilding from an older commit and hoping the result is equivalent — a different base image, a moved dependency or a changed build argument means it may not be. Worse, a rollback done by re-pointing the alias backwards is invisible: nothing in the registry records that the name used to mean something else. With both tags present, a rollback is boring: redeploy `indexer:7e1c94d`, which is a name nobody has permission to move, and adjust the alias afterwards if you keep one. ### What each side of the pair is *not* The build tag is not a version. It usually is not ordered — you cannot look at two commit-SHA tags and say which is newer — and it does not tell anybody whether a build is fit for production. That is what the alias, or a separate promotion step, is for. The alias is not an audit trail. It has no history: it is one pointer, and looking at it today tells you nothing about what it pointed at last Tuesday. If knowing that matters — and during an incident it usually does — the record has to live somewhere that keeps history, such as the deployment log that says which immutable tag was rolled out and when. ### The habit to take away Build once, push the immutable tag first, then move any aliases as a separate, deliberate step. Deploy and roll back by the immutable tag. Treat every mutable name as a signpost for humans, never as the thing a production system is pointed at.
- Does pushing the second tag upload the image again?No. `docker tag` only adds a name locally — both tags share one image ID and nothing is copied. The second `docker push` checks each layer against the repository first, finds them all present and reports "Layer already exists", so it uploads only a small manifest under the new tag. That is why re-pointing an alias is effectively free even for a large image.
- What happens to the build that `stable` used to point at?Nothing happens to the image itself. The tag is just a name, and re-pointing it removes that name from the old manifest — the manifest, its layers and its own build tag are untouched, so the old build is still pullable by `indexer:7e1c94d`. It only becomes genuinely unreachable if it had no other tag, which is precisely the situation the immutable build tag exists to prevent.
- Should the alias be moved before or after the deployment succeeds?After. The deployment itself should name the immutable tag, so the alias is a record of "this build is the one we are happy with", not the trigger. Moving it first means a failed rollout leaves the alias advertising a build that never worked, and anybody who pulls the alias in the meantime — a developer, a smoke test, a downstream build — gets the bad one.
A commit-SHA tag is like the permanent catalogue number printed on a book's spine; stable is like the "new arrivals" shelf. You can always find the book by its number, but the shelf tells you nothing about what stood there last month.
saying these in an interview costs you the question
- Thinks docker tag copies or rebuilds the image
- Believes re-pointing a tag deletes the old image
- Pushes only a moving alias and no per-build tag
- Treats an alias as a record of what shipped
- Says a rollback means rebuilding from the old commit
- Assumes two tags cost two uploads of the layers