Google Cloud Build: CI/CD Pipelines for GCP-Native Teams
Google Cloud Build: CI/CD Pipelines for GCP-Native Teams
If your stack runs on GCP — GKE, Cloud Run, Artifact Registry — Cloud Build is worth a serious look before you reach for GitHub Actions or Jenkins. It's serverless, tightly integrated with GCP IAM, and has no infrastructure to manage. You write a cloudbuild.yaml, and Google runs it.
This guide shows how to build a production-quality CI/CD pipeline with Cloud Build, including container builds, automated testing, Artifact Registry pushes, and GKE deployments.
How Cloud Build Works
A Cloud Build pipeline is a series of steps, each running in a Docker container. Steps share a workspace volume. You can use any Docker image as a step — Google provides official "cloud builders" for common tools, and you can bring your own.
Trigger (push/PR/tag)
→ Cloud Build picks up cloudbuild.yaml
→ Runs steps sequentially (or in parallel)
→ Pushes artifacts
→ Notifies (Pub/Sub, Cloud Logging)
Basic cloudbuild.yaml Structure
# cloudbuild.yaml
steps:
# Step 1: Install dependencies and run tests
- name: node:20
entrypoint: npm
args: ['ci']
id: install
- name: node:20
entrypoint: npm
args: ['test']
id: test
waitFor: ['install']
# Step 2: Build Docker image
- name: gcr.io/cloud-builders/docker
args:
- build
- -t
- $_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE:$SHORT_SHA
- -t
- $_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE:latest
- .
id: build-image
waitFor: ['test']
# Step 3: Push to Artifact Registry
- name: gcr.io/cloud-builders/docker
args:
- push
- --all-tags
- $_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE
id: push-image
waitFor: ['build-image']
# Step 4: Deploy to GKE
- name: gcr.io/cloud-builders/kubectl
args:
- set
- image
- deployment/$_SERVICE
- $_SERVICE=$_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE:$SHORT_SHA
- -n
- production
env:
- CLOUDSDK_COMPUTE_REGION=$_REGION
- CLOUDSDK_CONTAINER_CLUSTER=$_GKE_CLUSTER
id: deploy
waitFor: ['push-image']
images:
- $_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE:$SHORT_SHA
substitutions:
_REGION: us-central1
_REPO: app-images
_SERVICE: api-service
_GKE_CLUSTER: prod-cluster
options:
logging: CLOUD_LOGGING_ONLY
machineType: E2_HIGHCPU_8 # Bigger machine for faster builds
$PROJECT_ID and $SHORT_SHA are built-in substitutions. The $_ variables are user-defined and can be overridden per trigger.
Setting Up Artifact Registry
# Create a Docker repository in Artifact Registry
gcloud artifacts repositories create app-images \
--repository-format docker \
--location us-central1 \
--description "Application container images"
# Configure Docker auth for local dev
gcloud auth configure-docker us-central1-docker.pkg.dev
# Grant Cloud Build service account access
PROJECT_NUMBER=$(gcloud projects describe YOUR_PROJECT_ID --format='value(projectNumber)')
gcloud artifacts repositories add-iam-policy-binding app-images \
--location us-central1 \
--member "serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
--role roles/artifactregistry.writer
Configure Build Triggers
# Trigger on push to main branch
gcloud builds triggers create github \
--name "push-to-main" \
--repo-name YOUR_REPO \
--repo-owner YOUR_GITHUB_ORG \
--branch-pattern "^main$" \
--build-config cloudbuild.yaml \
--substitutions _SERVICE=api-service,_GKE_CLUSTER=prod-cluster
# Trigger on pull requests (run tests only, no deploy)
gcloud builds triggers create github \
--name "pull-request-ci" \
--repo-name YOUR_REPO \
--repo-owner YOUR_GITHUB_ORG \
--pull-request-pattern "^main$" \
--build-config cloudbuild-pr.yaml \
--comment-control COMMENTS_ENABLED_FOR_EXTERNAL_CONTRIBUTORS_ONLY
For PRs, use a separate cloudbuild-pr.yaml that runs tests and a security scan but skips the deploy:
# cloudbuild-pr.yaml
steps:
- name: node:20
entrypoint: npm
args: ['ci']
- name: node:20
entrypoint: npm
args: ['run', 'test:coverage']
- name: node:20
entrypoint: npm
args: ['run', 'lint']
- name: gcr.io/cloud-builders/docker
args: ['build', '-t', 'app:pr-$SHORT_SHA', '.']
- name: aquasec/trivy
args:
- image
- --exit-code
- '1'
- --severity
- HIGH,CRITICAL
- app:pr-$SHORT_SHA
options:
logging: CLOUD_LOGGING_ONLY
Parallel Steps for Faster Pipelines
By default, steps run sequentially. Use waitFor to run independent steps in parallel:
steps:
- name: node:20
entrypoint: npm
args: ['ci']
id: install
# These three run in parallel after install
- name: node:20
entrypoint: npm
args: ['run', 'test:unit']
id: unit-tests
waitFor: ['install']
- name: node:20
entrypoint: npm
args: ['run', 'lint']
id: lint
waitFor: ['install']
- name: node:20
entrypoint: npm
args: ['run', 'type-check']
id: type-check
waitFor: ['install']
# Build only after all checks pass
- name: gcr.io/cloud-builders/docker
args: ['build', '-t', 'gcr.io/$PROJECT_ID/app:$SHORT_SHA', '.']
id: build
waitFor: ['unit-tests', 'lint', 'type-check']
Using Cloud Build with Secret Manager
Never put secrets in cloudbuild.yaml. Reference them from Secret Manager:
steps:
- name: node:20
entrypoint: npm
args: ['run', 'integration-test']
secretEnv: ['DATABASE_URL', 'API_KEY']
availableSecrets:
secretManager:
- versionName: projects/$PROJECT_ID/secrets/database-url/versions/latest
env: DATABASE_URL
- versionName: projects/$PROJECT_ID/secrets/api-key/versions/latest
env: API_KEY
# Grant Cloud Build SA access to secrets
gcloud secrets add-iam-policy-binding database-url \
--member "serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
--role roles/secretmanager.secretAccessor
Private Pools for Compliance
By default, Cloud Build runs on shared Google infrastructure. For compliance requirements (accessing private VPC resources, no public internet), use a private pool:
# Create a private pool in your VPC
gcloud builds worker-pools create prod-pool \
--region us-central1 \
--peered-network projects/YOUR_PROJECT_ID/global/networks/default \
--worker-machine-type e2-standard-4 \
--worker-disk-size 100
# Reference in cloudbuild.yaml
# options:
# pool:
# name: projects/YOUR_PROJECT_ID/locations/us-central1/workerPools/prod-pool
Private pools can reach resources in your VPC — Cloud SQL, private GKE API servers, internal services — without exposing them to the internet.
Build Notifications via Pub/Sub
# Cloud Build publishes build status to Pub/Sub by default
# Subscribe to get notified on failures
gcloud pubsub subscriptions create build-notifications \
--topic cloud-builds \
--push-endpoint https://your-webhook.example.com/cloud-build \
--ack-deadline 60
# Or use Cloud Build Notifiers (pre-built)
# https://github.com/GoogleCloudPlatform/cloud-build-notifiers
# Available: Slack, email, GitHub commit status, HTTP
Build Caching
Cloud Build builds are stateless. Without caching, npm install or pip install runs from scratch every time:
steps:
# Pull cache image (fails silently if not found)
- name: gcr.io/cloud-builders/docker
entrypoint: bash
args:
- -c
- docker pull $_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE:cache || true
- name: gcr.io/cloud-builders/docker
args:
- build
- --cache-from
- $_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE:cache
- -t
- $_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE:$SHORT_SHA
- -t
- $_REGION-docker.pkg.dev/$PROJECT_ID/$_REPO/$_SERVICE:cache
- .
This pulls the previous image as a cache layer. Docker's layer caching then skips unchanged layers — npm install becomes fast when package-lock.json hasn't changed.
Pricing at a Glance
| Machine Type | vCPUs | RAM | Cost/min |
|---|---|---|---|
| e2-medium (default) | 1 | 4GB | $0.003 |
| E2_HIGHCPU_8 | 8 | 8GB | $0.016 |
| E2_HIGHCPU_32 | 32 | 32GB | $0.064 |
| N1_HIGHCPU_8 | 8 | 7.2GB | $0.034 |
The first 120 build-minutes per day are free. A typical Node.js build on E2_HIGHCPU_8 takes 3-5 minutes and costs $0.05-0.08. For a team of 10 developers pushing 20 times a day, that's roughly $20-30/month — cheaper than a Jenkins server.
Was this article helpful?
SRE & Observability Engineer
If it's not measured, it doesn't exist. SLO-driven, metrics-obsessed, and the person who gets paged at 3 AM so you don't have to. Observability isn't optional.
Related Articles
Google Cloud Run: Serverless Containers That Scale to Zero
Deploy and manage containerized workloads on Google Cloud Run — no infrastructure to manage. Covers service configuration, traffic splitting, secrets, VPC connectivity, Cloud Run Jobs, and cost optimization.
Google GKE: Production Kubernetes Cluster on Google Cloud
Launch a production GKE cluster on Google Cloud Platform — Autopilot vs Standard mode, node pools, Workload Identity, Binary Authorization, GKE Dataplane V2, private clusters, and Terraform configuration.
GCP Cloud Function "Function Execution Took Too Long" Error: Memory And Timeout Configuration Fix Guide
Getting the dreaded "Function execution took too long" error in your Cloud Functions? You're not alone. This error happens when your function exceeds the c...
Fix GCP Cloud Storage '403 Forbidden' Errors
Troubleshoot and fix Google Cloud Storage 403 Forbidden errors caused by missing IAM roles, service account misconfiguration, or uniform bucket-level access.
GKE Autopilot: Serverless Kubernetes on Google Cloud
Deploy production workloads on GKE Autopilot — serverless Kubernetes with automatic node management, security, and scaling.
GCP Core Services: The DevOps Engineer's Essential Guide
Learn GCP's core services — Compute Engine, GKE, Cloud Storage, VPC, IAM, Cloud Build, and Cloud Run for modern DevOps workflows.