Cloud Run’s immutability is the secret sauce that lets you roll back fearlessly.

Imagine you’ve deployed a new version of your service, and it’s not behaving. Maybe it’s crashing, returning incorrect data, or just plain slow. Cloud Run keeps every deployment, every "revision," as a distinct, unchangeable snapshot. Rolling back means telling Cloud Run to simply point the service’s traffic back to one of those older, known-good snapshots.

Let’s see this in action. We have a simple "hello, world" service.

# First, deploy an initial version
gcloud run deploy hello-world \
  --image gcr.io/cloudrun/hello \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated

# Output will show the service URL. Let's assume it's:
# Service [hello-world] revision [hello-world-00001] has been deployed successfully.
# URL: https://hello-world-abcdefg-uc.a.run.app

Now, we’ll deploy a "bad" version that always returns an error.

# Build and push a new image that errors out
# (For demonstration, we'll use a pre-built image that does this)
gcloud run deploy hello-world \
  --image gcr.io/cloudrun/helloworld-error \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated

# Output will show a new revision:
# Service [hello-world] revision [hello-world-00002] has been deployed successfully.
# URL: https://hello-world-abcdefg-uc.a.run.app

If you hit the URL now, you’d get an error. We need to go back.

To roll back, you identify the revision you want to restore. Cloud Run automatically assigns revision names like service-name-00001, service-name-00002, etc., with 00001 being the oldest. You can list them:

gcloud run revisions list --service hello-world --region us-central1

This will show something like:

REVISION   DEPLOYED             TRAFFIC
hello-world-00002   2023-10-27T10:00:00Z   100%
hello-world-00001   2023-10-27T09:00:00Z   0%

Notice the TRAFFIC column. Currently, hello-world-00002 has 100% of the traffic. We want to shift that 100% back to hello-world-00001.

The command to do this is gcloud run services update. You specify the service, the region, and crucially, you tell it which revision should receive 100% of the traffic.

gcloud run services update hello-world \
  --revision hello-world-00001 \
  --traffic 100 \
  --region us-central1

After this command, the gcloud run revisions list output would look like:

REVISION   DEPLOYED             TRAFFIC
hello-world-00002   2023-10-27T10:00:00Z   0%
hello-world-00001   2023-10-27T09:00:00Z   100%

And if you hit your service URL again, it will now be serving traffic from hello-world-00001, the good version.

The mental model here is that a Cloud Run "service" is just a pointer. It’s a name that, at any given moment, directs traffic to one specific revision. Revisions themselves are immutable Docker images with their configurations. When you deploy, you’re creating a new immutable revision and potentially updating the "service" pointer to point to it. When you roll back, you’re simply moving that pointer back to an older, already-existing revision. You can even split traffic between multiple revisions (e.g., 90% to the stable version, 10% to a new beta version for testing).

This immutability and traffic management is what makes Cloud Run so robust for deployments. You never truly lose a previous state. You can always revert.

The one thing that trips people up is forgetting that gcloud run services update doesn’t delete the bad revision; it just stops sending traffic to it. The old revision remains available, stored as a snapshot, ready to be reassigned traffic later if needed. This is why listing revisions shows both the active and inactive ones.

The next step is understanding how to manage traffic splitting for canary deployments.

Want structured learning?

Take the full Cloud-run course →