Why Senior Engineers Never Use latest for Container Images
3 min read

You've just deployed a new version of your application. Everything looks good. But... something breaks.
You check Kubernetes, ECS, or whatever you're running your containers on and see:
my-app:latest
Great.
But what version is actually running? What version caused this incident?
That's exactly why I avoid using the latest tag for container images.
latest doesn't mean latest
One of the first misconceptions I had about Docker image tags was that latest somehow meant the newest version of the application.
It doesn't.
latest is just a tag.
If I build an image today:
docker build -t my-app:latest .
docker push my-app:latestand then build another image tomorrow using the same tag, I've now pointed latest at a completely different image.
The name hasn't changed.
The underlying application has.
That's where the problems begin.
I want to know exactly what's running
Instead, I prefer immutable image tags.
In CI/CD, one of the easiest ways to achieve this is using the Git commit SHA.
For example:
my-app:a84f291
Now there is a direct relationship between Git commit to Container image to Deployment.
If production is running:
my-app:a84f291
I can go to Git, find commit a84f291, and see exactly what code produced that container.
No guessing.
No wondering which build overwrote latest.
No "but it worked yesterday".
Rollbacks save the day
Let's say I deploy:
my-app:f921bc3
and five minutes later my monitoring starts screaming.
With immutable tags, I already know the previous working version:
my-app:a84f291
So I can roll back to it.
I don't need to rebuild the application.
I don't need to figure out what latest used to contain.
The old artifact already exists.
It becomes even more important with GitOps
This matters even more when using tools like ArgoCD.
Imagine my Kubernetes manifest says:
containers:
- name: app
image: myregistry/my-app:latestThe Git repository says latest.
The cluster says latest.
But neither tells me which version of the application is actually running.
That undermines one of the biggest benefits of GitOps: Git should describe the desired state of my system.
Instead, I'd rather commit:
containers:
- name: app
image: myregistry/my-app:a84f291Now Git tells me exactly what should be running.
If CI builds a new image:
my-app:f921bc3
the deployment manifest can be updated to reference that specific image.
The change is visible.
It's reviewable.
It's auditable.
And it's reversible.
Traceability beats convenience
Using latest feels convenient because you don't have to think about versioning.
But you're trading a tiny amount of convenience for ambiguity.
In a real deployment, I want to be able to answer three questions quickly:
What is running?
Where did it come from?
How do I roll it back?
Immutable tags make all three much easier.
You don't necessarily have to use Git SHAs either. Semantic versions like v1.4.2, build numbers, or another immutable identifier can work.
The important part is that once a tag identifies an artifact, don't change what that tag points to.
I mean I'll happily use latest while experimenting locally.
But for an actual CI/CD pipeline?
Give every image an immutable identity.
Because when production breaks at 2 AM, "latest" is probably the least useful answer to:
"What version are we running?"