I moved a small side project from a single VM to Cloud Run mostly because I was tired of patching the box. This is the deploy pipeline I settled on, not the one I started with.
The first version was too clever
My initial pipeline built the image, pushed it, deployed, and then ran a smoke test. It broke constantly: the smoke test ran before the new revision had any traffic, the rollback was "redeploy the previous image" typed by hand, and I once deployed on a Friday because I wasn't paying attention.
What I have now
A GitHub Actions workflow with three steps:
- Build and push the image, tagged with the commit SHA. No
latest, ever. If I can't tell exactly what's running, I can't roll back. - Deploy with
--no-traffic, which creates a revision without sending it live. - Run the smoke test against the tagged revision URL, then move traffic with a single command.
The important part is step 2. Deploying a revision without traffic means a broken build never serves a single request, and the rollback is just "point traffic back at the previous revision" — one command, no rebuild.
Things that bit me
Cloud Run sets PORT for you. My app had a hardcoded 8080 and worked locally, then failed in production because the platform expected it to read the env var. Now I read process.env.PORT first, with a local default.
Health checks: I used to rely on the TCP check and it was useless. A real HTTP endpoint that checks the dependencies gives you a revision that fails to go healthy, which is far better than one that serves 500s.
Secrets: never in the image. The runtime service account pulls them from Secret Manager at boot, or they're injected as env vars at deploy. I had one accidental commit of a .env file early on and it scared me straight.
What I'd do with more traffic
Traffic splitting by percentage, and maybe a canary step between smoke test and full rollout. For a side project that nobody is paying for, the current version is enough: broken revisions don't get traffic, rollback is one command, and I can deploy from a phone if I have to.
Top comments (3)
Tagging by commit SHA is the part I would keep. It makes rollback a clear choice instead of guessing what latest means. Running the smoke test against the revision URL before moving traffic is what turns that into a safe deploy.
How long did the hardcoded PORT survive before it bit you?
The --no-traffic step is the piece most people skip. We do the same, and rollback has become one command.