CI/CD Pipeline for Cloud Run on GCP

This page shows you how to build a complete CI/CD pipeline for Cloud Run on GCP. You will end up with a working pipeline that runs tests, builds a Docker image, stores it in Artifact Registry, and deploys it to Cloud Run automatically whenever code is merged. Along the way, you will understand when to use Cloud Deploy, how to do safer production releases, and what common mistakes to avoid.

Simple explanation

A CI/CD pipeline is automation that moves your code from your repository to a running service, without anyone running commands by hand. The basic flow is:

  1. A developer pushes or merges code.
  2. Automated tests run to check nothing is broken.
  3. A Docker image is built from the code.
  4. The image is stored in Artifact Registry.
  5. Cloud Run is told to use the new image, and a new revision starts.
  6. More careful teams add a verification or approval step before production traffic shifts.

The result is that every code change that passes your tests ends up running in production, repeatably and with a full audit trail, in a matter of minutes.

How to picture it

Think of a CI/CD pipeline like a parcel courier service. You hand a package to the depot (push code). The depot checks it meets shipping requirements (tests). A tracking label is printed with a unique ID (image tagged with the commit SHA). It goes into the warehouse (Artifact Registry). A driver delivers it to the address (Cloud Run). You never drive it yourself, and every handoff is logged.

How it works

Each pipeline run follows a fixed sequence. Here is the end-to-end flow:

  1. Developer pushes code to the main branch, or merges a pull request.
  2. Cloud Build trigger fires and starts the pipeline automatically.
  3. Tests run first. The pipeline stops here if they fail, before spending time on a build.
  4. Docker image builds, tagged with the short commit SHA so every image is traceable to a specific change.
  5. Image pushes to Artifact Registry, the private container registry inside your GCP project.
  6. Cloud Run is updated using either a direct gcloud run deploy call, or a Cloud Deploy release creation.
  7. Optional: verification and traffic shift. Smoke tests run against a tagged revision URL, then traffic shifts after they pass.
  8. Cloud Build logs the full run. Every step is recorded and searchable in the console.

What each component does

Cloud Build

Cloud Build runs your pipeline steps in a managed environment. It has native integration with GCP, so it can push images and deploy services without storing external credentials. See the Cloud Build overview for how triggers and build steps work.

Artifact Registry

Artifact Registry is your private Docker image store. Every image your pipeline builds gets pushed here before deployment. Cloud Run pulls from it directly. It also scans for vulnerabilities and supports fine-grained access control per repository.

Cloud Run

Cloud Run is the deployment target. It runs your container, manages revisions, and handles traffic routing. A new revision starts on each deployment. Traffic shifts to the new revision once it passes startup health checks.

Cloud Deploy

Cloud Deploy handles the promotion of a release through multiple environments. It adds approval gates, a defined promotion workflow, and rollback capability. You only need it if you are managing more than one environment or want manual approval before production.

Why Cloud Run is a good starting point

Cloud Run is one of the simplest GCP services to deploy to from a pipeline. A single gcloud run deploy command updates a running service with a new image. There is no cluster to manage, no deployment manifest to maintain, and no node pool to worry about.

This makes it a practical first target if you are building your first GCP CI/CD pipeline. The core patterns (building images, pushing to Artifact Registry, deploying via gcloud) apply directly when you later move to more complex targets like GKE. You also get zero-downtime deployments and a complete audit trail with no extra configuration.

Complete pipeline example

The example below is a complete cloudbuild.yaml for a Python Cloud Run service. It runs tests first, builds the image only if they pass, pushes to Artifact Registry, then deploys to Cloud Run. Replace YOUR_PROJECT_ID, YOUR_REGION, and YOUR_SERVICE_NAME with your own values before using it.

steps:
  # Run tests first — fail fast before spending time building an image
  - name: 'python:3.11'
    entrypoint: bash
    args:
      - -c
      - |
        pip install -r requirements.txt
        python -m pytest tests/ -v
    id: run-tests

  # Build Docker image — only after tests pass
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - build
      - -t
      - YOUR_REGION-docker.pkg.dev/YOUR_PROJECT_ID/api/api:$SHORT_SHA
      - .
    id: build-image
    waitFor: ['run-tests']

  # Push to Artifact Registry
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'YOUR_REGION-docker.pkg.dev/YOUR_PROJECT_ID/api/api:$SHORT_SHA']
    id: push-image
    waitFor: ['build-image']

  # Deploy to Cloud Run
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    args:
      - gcloud
      - run
      - deploy
      - YOUR_SERVICE_NAME
      - --image=YOUR_REGION-docker.pkg.dev/YOUR_PROJECT_ID/api/api:$SHORT_SHA
      - --region=YOUR_REGION
      - --platform=managed
    waitFor: ['push-image']

images:
  - 'YOUR_REGION-docker.pkg.dev/YOUR_PROJECT_ID/api/api:$SHORT_SHA'

A few things worth understanding before you adapt this file:

  • Tests run first because they are the cheapest thing to run. If they fail, no image is built and no Cloud Build minutes are spent on a Docker build.
  • $SHORT_SHA tags every image with the commit that produced it. This makes it straightforward to trace any running Cloud Run revision back to a specific change in your repository.
  • waitFor enforces strict execution order. Without it, Cloud Build may run steps in parallel. This pipeline must be sequential: tests, then build, then push, then deploy.
  • The gcr.io/google.com/cloudsdktool/cloud-sdk builder is the current maintained image for running gcloud commands. The older gcr.io/cloud-builders/gcloud image is deprecated; do not use it in new pipelines.

For more on structuring test steps and running them efficiently, see automated testing in Cloud Build.

Required permissions

The Cloud Build service account needs three IAM roles to run this pipeline. Grant only these and nothing broader:

# Get the Cloud Build default service account email
CB_SA=$(gcloud builds get-default-service-account --project=YOUR_PROJECT_ID)

# Push images to Artifact Registry
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
  --member="serviceAccount:${CB_SA}" \
  --role="roles/artifactregistry.writer"

# Deploy Cloud Run services
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
  --member="serviceAccount:${CB_SA}" \
  --role="roles/run.developer"

# Act as the Cloud Run service's identity (required for deploy to succeed)
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
  --member="serviceAccount:${CB_SA}" \
  --role="roles/iam.serviceAccountUser"

Why each role exists:

  • artifactregistry.writer lets Cloud Build push the built image to your private registry. Without it, the push step fails with a permission denied error.
  • run.developer lets Cloud Build create new revisions and update the Cloud Run service. It does not grant any broader project-level access.
  • iam.serviceAccountUser allows Cloud Build to act as the Cloud Run service account during deployment. Without it, the deploy step fails even if the other two roles are granted.
Do not use the default service account

The default compute service account has editor-level access to your project. If a Cloud Run service using it is ever compromised, an attacker gains broad access to your infrastructure. Create a dedicated, least-privilege service account for each service and set it explicitly at deploy time. See service accounts on GCP for how to do this.

When to use this

This pattern works well for:

  • Small to mid-sized teams shipping containerised APIs or web services to Cloud Run.
  • Teams that want fast, repeatable deployments with a full audit trail and no manual steps.
  • Projects starting with a single production environment and planning to add staging or dev environments later.
  • Teams already using Cloud Build for CI who want to extend the same pipeline to cover deployment.

When this is not enough

The direct Cloud Build approach starts to show its limits when:

  • You have multiple environments and need controlled, auditable promotion between them.
  • You need a human approval gate before anything reaches production.
  • You want rollback capability that is tracked and repeatable from the console or API.
  • Your release process involves progressive delivery strategies like canary or blue-green deployments.
  • Compliance or change control requirements mean you need a full release history.

In those situations, the next step is managing environments in CI/CD and adding Cloud Deploy to your pipeline.

Cloud Build vs Cloud Deploy for Cloud Run

Both tools can deploy to Cloud Run. The question is how much structure you need around the release process.

Use Cloud Build directly when:

  • You have one environment, or you treat each GCP project as a standalone deployment.
  • Automated promotion is acceptable, with no approval gates needed before production.
  • You want the simplest possible setup to get started quickly.

Add Cloud Deploy when:

  • You are promoting releases through dev, staging, and production in a defined sequence.
  • You want manual approval before any rollout reaches production.
  • You need tracked rollback history: the ability to promote a previous release to a target with a single command.
  • You want rollout events and status visible in the Cloud Deploy console, separate from your CI build logs.

The two tools are designed to work together. In a mature pipeline, Cloud Build ends with a step that creates a Cloud Deploy release. Cloud Deploy then takes over, promoting the release through environments with whatever gates you have defined. Cloud Build handles CI; Cloud Deploy handles CD.

Start simple

You do not need Cloud Deploy on day one. Start with a direct gcloud run deploy step in Cloud Build. You can add Cloud Deploy later without rewriting your build pipeline. The two parts are independent, and adding Cloud Deploy is an extension, not a replacement.

See the Cloud Deploy overview and the guide to deploying with Cloud Build for more detail on each approach.

Safer production releases

The simplest pipeline shifts 100% of traffic to the new revision as soon as it deploys. For low-risk internal services that is often fine. For production APIs, you usually want to verify the new revision before any users reach it.

Cloud Run supports deploying a revision without giving it any traffic. The revision starts and is accessible at a stable tagged URL, but production traffic continues going to the current revision. You run smoke tests against the tagged URL, and only if they pass do you shift traffic across.

The new kitchen test

Imagine a restaurant opening a refitted kitchen. The old kitchen keeps serving tables throughout the evening. The new kitchen runs on the same shift, but only the head chef eats from it first. If the dishes come out right, the old kitchen switches off and the new one takes over service. No diner ever receives a meal from an unverified kitchen.

  # Deploy new revision with no live traffic — accessible at tagged URL only
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    args:
      - gcloud
      - run
      - deploy
      - YOUR_SERVICE_NAME
      - --image=YOUR_REGION-docker.pkg.dev/YOUR_PROJECT_ID/api/api:$SHORT_SHA
      - --region=YOUR_REGION
      - --no-traffic
      - --tag=canary

  # Shift 100% of traffic to the new revision after verification
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    args:
      - gcloud
      - run
      - services
      - update-traffic
      - YOUR_SERVICE_NAME
      - --region=YOUR_REGION
      - --to-latest

Between those two steps is where your smoke tests belong. Add a step that calls a health endpoint, runs a lightweight integration test, or simply checks that the service responds at the tagged URL. If the smoke test step fails, the pipeline stops and the traffic shift never happens.

For teams who want a human to approve before traffic shifts, that decision belongs in Cloud Deploy rather than a shell script in Cloud Build. See rollbacks in Cloud Deploy for how approval gates and rollback work together.

For gradual traffic migration rather than a full cutover, see canary deployments and blue-green deployments.

Multi-environment pipelines with Cloud Deploy

When you have dev, staging, and production environments, replace the direct deploy step with a Cloud Deploy release creation. Cloud Deploy then manages promotion through each stage, with any approval gates you have configured:

  # Create a Cloud Deploy release instead of deploying directly
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    args:
      - gcloud
      - deploy
      - releases
      - create
      - release-$SHORT_SHA
      - --delivery-pipeline=YOUR_PIPELINE_NAME
      - --region=YOUR_REGION
      - --images=api=YOUR_REGION-docker.pkg.dev/YOUR_PROJECT_ID/api/api:$SHORT_SHA

This creates a Cloud Deploy release that automatically deploys to your dev environment. Once dev looks healthy, you promote to staging, then to production through the approval gate. The same image SHA that passed dev is what eventually reaches production. Nothing is rebuilt between environments.

PR preview environments

A preview environment deploys each pull request to a unique, temporary Cloud Run URL. A reviewer can test the changes against real infrastructure before the PR merges, without checking out the branch and running it locally.

Cloud Run’s tagged revisions make this practical. You deploy the PR to a service in your dev project, using a tag derived from the PR reference or short SHA. The tagged revision gets a stable URL. Post that URL as a comment on the pull request so reviewers can navigate directly to the preview.

steps:
  - name: 'gcr.io/cloud-builders/docker'
    args:
      - build
      - -t
      - YOUR_REGION-docker.pkg.dev/YOUR_DEV_PROJECT/api/api:$SHORT_SHA
      - .

  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'YOUR_REGION-docker.pkg.dev/YOUR_DEV_PROJECT/api/api:$SHORT_SHA']

  # Deploy to dev project with a PR-specific tag — no production traffic, ever
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    args:
      - gcloud
      - run
      - deploy
      - api-service
      - --image=YOUR_REGION-docker.pkg.dev/YOUR_DEV_PROJECT/api/api:$SHORT_SHA
      - --region=YOUR_REGION
      - --no-traffic
      - --tag=pr-$SHORT_SHA
      - --project=YOUR_DEV_PROJECT

A few things to get right with preview environments:

  • Always deploy to a dev project, not production. Preview revisions should be completely isolated from your production service and billing account.
  • Write a clean-up workflow. Set up a separate trigger that fires when a pull request closes and removes the tagged revision. Idle preview revisions accumulate and attract a small but unnecessary ongoing cost.
  • Never shift production traffic from a PR pipeline. The —no-traffic flag keeps the preview revision accessible only at its tagged URL. Production users are never affected.
Clean up after closed PRs

If you do not automate clean-up, tagged revisions from merged or closed PRs will keep accumulating in your dev project. Each one sits idle but still counts toward your Cloud Run instance limits and adds a small amount to your bill. Add a pipeline step triggered on PR close that deletes the revision tag or scales the revision to zero.

For secrets that preview environments need (API keys, database credentials, and similar), see secrets in CI/CD pipelines for how to inject them safely without logging them in the build output.

Common mistakes

  1. Deploying to production on every branch push. Configure your Cloud Build trigger to fire only on pushes to main or your release branch. Feature branches should run tests only. Without branch filters, pushing a work-in-progress branch can trigger a production deployment.

  2. Building the Docker image before running tests. If tests fail, you have spent Cloud Build minutes on an image you will throw away. Put the test step first and use waitFor to ensure the build only starts after tests pass.

  3. Using the default compute service account for Cloud Run. The default service account has broader permissions than any individual service needs. Create a dedicated, least-privilege account for each Cloud Run service and set it explicitly at deploy time.

  4. Shifting traffic before verifying behaviour. A Cloud Run startup health check only confirms the container started. It does not verify that your application responds correctly to real requests. Test the tagged revision URL before shifting production traffic.

  5. Running preview environments in the production project. Preview revisions accumulate in your revision list, add noise to your logs, and share billing with production. Use a separate dev project for all PR deployments.

  6. Hardcoding project IDs, regions, and service names without documenting them. When someone needs to reproduce the pipeline in a different environment, undocumented hardcoded values cause confusion. Use Cloud Build substitution variables or add a clear comment whenever a value is environment-specific.

Tool

Before you automate deployments, estimate what your workload will cost. Use the Cloud Run cost calculator to model request volume and resource settings so there are no billing surprises after launch.

Frequently asked questions

What triggers a Cloud Run CI/CD pipeline?

A Cloud Build trigger watches your source repository for push or merge events. When you push to main, the trigger fires and the pipeline starts automatically. For production, use a tag trigger or require a manual Cloud Deploy promotion instead of auto-deploying on every push.

Should I use Cloud Build or Cloud Deploy for Cloud Run CD?

For a single environment, a gcloud run deploy step in Cloud Build is simpler and needs less setup. For multi-environment pipelines with dev, staging, and production and with approval gates, add Cloud Deploy. Start simple and graduate to Cloud Deploy when the complexity justifies it.

How do I avoid downtime during a Cloud Run deployment?

Cloud Run performs zero-downtime deployments by default — new revisions come up before traffic shifts. For extra safety, use --no-traffic to deploy the revision first, run smoke tests against the tagged URL, then shift traffic only after verification.

What service account should my Cloud Run service use?

Create a dedicated, least-privilege service account for each Cloud Run service. The default compute service account has broader permissions than any individual service needs. Set it explicitly in your gcloud run deploy command or Terraform configuration.

Can I create preview environments for pull requests?

Yes. Cloud Run tagged revisions make PR preview environments practical. A pipeline triggered on pull request events builds and deploys each PR to a unique tagged URL in a dev project. Reviewers test against real infrastructure before the PR merges.

Last verified: 25 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.