How to Roll Back a Bad Deploy — and What Won't Roll Back With It
If you deployed something broken, the fastest fix is usually to go back rather than forward. On the Docker Push path that's one click: the app's Deployment tab lists every image you've pushed, with its tag and push date, and a Use this image button that switches the app to it. For git and zip deploys, rolling back means redeploying the previous commit or artifact. The catch worth planning for is that only your code rolls back — database migrations, volume data, and anything you sent to the outside world stay exactly where the bad deploy left them.
The one-click path: pushed images
If you deploy by pushing images to the built-in registry, you already have a rollback mechanism. The app's Deployment tab shows a table of everything pushed to that app's repository — tag, when it was pushed, and size — and each row has a Use this image button.
Clicking it points the app at that image and restarts onto it — the same mechanism as deploying from any Docker image, just pointed backwards. That's the whole procedure: find the tag that worked, click, wait for the restart.
This is the strongest argument for using the built-in registry: the registry and the runtime being the same system is what makes your deploy history browsable from the app itself.
Tagging latest throws the option away
Here's the mistake that removes your ability to roll back, and it's the default habit for a lot of people.
docker push myapp:latest overwrites the tag every time. Push twice and there's only ever one image called latest — the last one. The table shows you a single row, and there's nothing earlier to switch to.
Tag with something immutable:
docker build -t <registry>/<repo>:$(git rev-parse --short HEAD) .
docker push <registry>/<repo>:$(git rev-parse --short HEAD)
A commit SHA or a version number gives every deploy its own row, and rollback becomes a matter of recognising which one was fine. It also tells you exactly what code is running, which is worth as much as the rollback.
If you like latest as a convenience pointer, push both — the SHA tag is what preserves history.
Rolling back a git or zip deploy
These paths build the image for you, so there's no tag list to pick from. Rollback is redeploying what worked:
-
Git push: push the last good commit to the deploy branch. Force-pushing an older commit is normal here — the deploy remote isn't tracking your history, you're declaring what should be live.
git push --force "$DEPLOY_URL" <good-sha>:deploy -
Zip upload: re-upload the previous archive, which means keeping it. If you deploy by zip, keep the last known-good zip somewhere — it's the only copy of that build.
Both rebuild rather than reusing the earlier image, so they take as long as a normal deploy and depend on your dependencies still resolving the same way. That's a real difference from the image path, where you're switching to an artifact that already exists.
What doesn't roll back
This is the part that turns a quick fix into an incident, so decide it before you click:
Database migrations. If the bad deploy added a column, dropped one, or rewrote rows, going back to the old image leaves the new schema in place. Old code meeting a new schema can be worse than the bug you're fixing — a dropped column is unrecoverable without a backup. Write migrations so the previous version of your code can still run against the new schema, and you turn rollback into a safe operation.
Volume data. Everything your app wrote is still there. Rolling back the code doesn't unwrite files.
External side effects. Messages posted, emails sent, webhooks delivered, payments taken. Nothing reverses those.
In-flight work. A rollback is a restart, so anything mid-execution is killed about a second after the stop signal.
The arm64 trap when you rebuild
Worth knowing, because rollback sometimes means rebuilding under pressure: images must be built for linux/x64. Building on an Apple Silicon Mac produces an arm64 image by default, which won't run — the panel checks and tells you so rather than letting you deploy something that can't start.
Under time pressure that's an easy hour to lose. Build multi-arch or target the platform explicitly:
docker build --platform linux/amd64 -t <registry>/<repo>:$(git rev-parse --short HEAD) .
Better still, build in CI where the runner is x64 anyway — which is one push in a workflow file.
Make rollback boring
Three habits, none of which cost anything:
- Immutable tags, so history exists.
- Backwards-compatible migrations, so going back is safe rather than a second incident.
- Know your last good tag — write it in the deploy notes, or just recognise it in the table. Under pressure, "which one worked?" is the slow part, not the clicking.
And if the fix is genuinely quick, rolling forward is fine. The point of a rollback isn't ceremony, it's having a fast option that doesn't require you to debug correctly on the first try.
FAQ
How do I roll back a deploy?
On the Docker Push path, open the app's Deployment tab, find the previous tag in the image list, and click Use this image. For git or zip deploys, redeploy the previous commit or archive.
Why can't I see older images to roll back to?
You're probably tagging latest, which overwrites each push. Tag with a commit SHA or version so each deploy keeps its own entry.
Does rolling back undo my database migration?
No. Only the running image changes. Schema changes, volume data, and anything sent externally stay as they are — write migrations that older code can still tolerate.
Is there downtime when rolling back?
Yes, the same as any deploy — the container is replaced, so there's a short gap.
Why won't my image start after I built it on a Mac?
Apple Silicon builds arm64 by default and these are linux/x64 containers. Build with --platform linux/amd64, or build in CI.
Should I roll back or fix forward?
Roll back if you don't know the cause yet; fix forward if the fix is obvious and small. Rollback buys you time to diagnose without users waiting.
Wrapping up
Rollback is one of those capabilities that's either there when you need it or isn't, and the deciding factor is a decision you made calmly weeks earlier: whether your tags are unique and whether your migrations are reversible in practice. Get those two right and a bad deploy is a click and a short restart. Get them wrong and it's an archaeology exercise with users waiting.