Google Cloud
intermediate
30-40 minutes
Cloud Build CI/CD
Automate builds and deployments with Cloud Build - Google's serverless CI/CD platform.
What you'll accomplish
Automated build pipeline that deploys on every push to your repository
Prerequisites
- GCP project with billing enabled
- gcloud CLI configured
- GitHub or Cloud Source Repository
What is Cloud Build?
Cloud Build is a serverless CI/CD platform that executes your builds on Google Cloud infrastructure.
How It Works
Code Push → Trigger → Cloud Build → Deploy
│ │ │ │
└── Git ────┴── Config ─┴── Steps ──┴── Cloud Run/GKE/etc
Key Features
- Serverless - No servers to manage
- Pay per use - First 120 build-minutes/day free
- Fast - Parallel step execution
- Flexible - Any Docker image as build step
- Integrated - Native GCP service integration
Cloud Build vs Other CI/CD
| Feature | Cloud Build | GitHub Actions | Jenkins |
|---|---|---|---|
| Hosting | Serverless | Serverless | Self-hosted |
| GCP integration | Native | Via actions | Via plugins |
| Free tier | 120 min/day | 2000 min/month | N/A |
| Configuration | cloudbuild.yaml | workflow.yaml | Jenkinsfile |
Step 1: Enable Cloud Build API
bashgcloud services enable cloudbuild.googleapis.com
Step 2: Connect Your Repository
Option A: GitHub
- Go to Cloud Build → Triggers → Connect Repository
- Select GitHub (Cloud Build GitHub App)
- Authenticate with GitHub
- Select your repository
- Accept the connection
Option B: Cloud Source Repositories
bash# Create a Cloud Source Repository gcloud source repos create my-repo # Clone the repository gcloud source repos clone my-repo # Or mirror from GitHub # Go to Cloud Source Repositories → Add repository → Connect external repository
Option C: GitLab or Bitbucket
Use the Cloud Build integration available in:
- Cloud Build → Triggers → Connect Repository
- Select your provider and authenticate
Step 3: Create Build Configuration
Basic cloudbuild.yaml
Create cloudbuild.yaml in your repository root:
yamlsteps: # Install dependencies - name: 'node:20' entrypoint: 'npm' args: ['ci'] # Run tests - name: 'node:20' entrypoint: 'npm' args: ['test'] # Build the application - name: 'node:20' entrypoint: 'npm' args: ['run', 'build']
Build and Push Docker Image
yamlsteps: # Build Docker image - name: 'gcr.io/cloud-builders/docker' args: - 'build' - '-t' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - '-t' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:latest' - '.' # Push images to Artifact Registry images: - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:latest'
Deploy to Cloud Run
yamlsteps: # Build Docker image - name: 'gcr.io/cloud-builders/docker' args: - 'build' - '-t' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - '.' # Push to Artifact Registry - name: 'gcr.io/cloud-builders/docker' args: - 'push' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' # Deploy to Cloud Run - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' entrypoint: 'gcloud' args: - 'run' - 'deploy' - 'myapp' - '--image' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - '--region' - 'us-central1' - '--platform' - 'managed' - '--allow-unauthenticated' images: - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA'
Step 4: Create a Build Trigger
Via Console
- Go to Cloud Build → Triggers
- Click Create Trigger
- Configure:
- Name:
deploy-on-push - Event: Push to a branch
- Source: Your connected repository
- Branch:
^main$(regex) - Configuration: Cloud Build configuration file
- Location:
/cloudbuild.yaml
- Name:
Via CLI
bashgcloud builds triggers create github \ --repo-name=my-repo \ --repo-owner=my-github-user \ --branch-pattern="^main$" \ --build-config=cloudbuild.yaml \ --name=deploy-on-push
Trigger Types
| Type | When It Runs |
|---|---|
| Push to branch | On push matching branch pattern |
| Push new tag | On new tag matching pattern |
| Pull request | On PR open/update (preview builds) |
| Manual | Manual trigger only |
| Webhook | External webhook call |
Step 5: Grant Permissions
Cloud Build needs permissions to deploy:
bash# Get the Cloud Build service account PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)') CLOUD_BUILD_SA="${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" # Grant Cloud Run Admin gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:${CLOUD_BUILD_SA}" \ --role="roles/run.admin" # Grant Service Account User (to act as the runtime service account) gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:${CLOUD_BUILD_SA}" \ --role="roles/iam.serviceAccountUser" # Grant Artifact Registry Writer gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:${CLOUD_BUILD_SA}" \ --role="roles/artifactregistry.writer"
Step 6: Run and Monitor Builds
Manual Build
bash# Submit a build manually gcloud builds submit --config=cloudbuild.yaml . # Submit with substitutions gcloud builds submit \ --config=cloudbuild.yaml \ --substitutions=_ENV=production,_VERSION=1.0.0 \ .
View Build History
bash# List recent builds gcloud builds list --limit=10 # View specific build gcloud builds describe BUILD_ID # Stream build logs gcloud builds log BUILD_ID --stream
View in Console
- Go to Cloud Build → History
- Click on a build to see:
- Step-by-step execution
- Logs for each step
- Artifacts produced
- Timing information
Advanced Configuration
Substitution Variables
Built-in variables:
| Variable | Description |
|---|---|
$PROJECT_ID | GCP project ID |
$BUILD_ID | Unique build ID |
$COMMIT_SHA | Full commit SHA |
$SHORT_SHA | First 7 characters of commit |
$REPO_NAME | Repository name |
$BRANCH_NAME | Branch name |
$TAG_NAME | Tag name (if triggered by tag) |
$REVISION_ID | Commit revision |
Custom substitutions:
yamlsubstitutions: _ENV: staging _REGION: us-central1 steps: - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' args: - 'gcloud' - 'run' - 'deploy' - 'myapp-${_ENV}' - '--region=${_REGION}'
Parallel Steps
yamlsteps: # These run in parallel - name: 'node:20' id: 'lint' entrypoint: 'npm' args: ['run', 'lint'] - name: 'node:20' id: 'test' entrypoint: 'npm' args: ['test'] # This waits for both - name: 'node:20' id: 'build' entrypoint: 'npm' args: ['run', 'build'] waitFor: ['lint', 'test']
Environment Variables
yamlsteps: - name: 'node:20' entrypoint: 'npm' args: ['run', 'build'] env: - 'NODE_ENV=production' - 'API_URL=https://api.example.com'
Secrets from Secret Manager
yamlsteps: - name: 'node:20' entrypoint: 'npm' args: ['run', 'deploy'] secretEnv: ['API_KEY', 'DATABASE_URL'] availableSecrets: secretManager: - versionName: 'projects/$PROJECT_ID/secrets/API_KEY/versions/latest' env: 'API_KEY' - versionName: 'projects/$PROJECT_ID/secrets/DATABASE_URL/versions/latest' env: 'DATABASE_URL'
Grant access to secrets:
bashgcloud secrets add-iam-policy-binding API_KEY \ --member="serviceAccount:${CLOUD_BUILD_SA}" \ --role="roles/secretmanager.secretAccessor"
Build Timeout
yamltimeout: '1200s' # 20 minutes steps: - name: 'node:20' timeout: '300s' # 5 minutes for this step entrypoint: 'npm' args: ['test']
Machine Type
yamloptions: machineType: 'E2_HIGHCPU_8' # Faster builds diskSizeGb: 100 # More disk space steps: - name: 'node:20' entrypoint: 'npm' args: ['run', 'build']
Available machine types:
E2_MEDIUM(default)E2_HIGHCPU_8E2_HIGHCPU_32
Complete Example Configurations
Next.js to Cloud Run
yaml# cloudbuild.yaml steps: # Install dependencies - name: 'node:20' entrypoint: 'npm' args: ['ci'] # Run linting - name: 'node:20' id: 'lint' entrypoint: 'npm' args: ['run', 'lint'] # Run tests - name: 'node:20' id: 'test' entrypoint: 'npm' args: ['test'] waitFor: ['-'] # Run in parallel with lint # Build Docker image - name: 'gcr.io/cloud-builders/docker' args: - 'build' - '-t' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/nextjs-app:$COMMIT_SHA' - '-t' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/nextjs-app:latest' - '--build-arg' - 'NEXT_PUBLIC_API_URL=${_API_URL}' - '.' waitFor: ['lint', 'test'] # Push image - name: 'gcr.io/cloud-builders/docker' args: - 'push' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/nextjs-app:$COMMIT_SHA' # Deploy to Cloud Run - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' entrypoint: 'gcloud' args: - 'run' - 'deploy' - 'nextjs-app' - '--image' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/nextjs-app:$COMMIT_SHA' - '--region' - 'us-central1' - '--platform' - 'managed' - '--allow-unauthenticated' - '--memory' - '512Mi' - '--cpu' - '1' substitutions: _API_URL: 'https://api.example.com' images: - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/nextjs-app:$COMMIT_SHA' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/nextjs-app:latest' options: machineType: 'E2_HIGHCPU_8'
Multi-Environment Deployment
yaml# cloudbuild-staging.yaml steps: - name: 'gcr.io/cloud-builders/docker' args: - 'build' - '-t' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - '.' - name: 'gcr.io/cloud-builders/docker' args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA'] - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' entrypoint: 'gcloud' args: - 'run' - 'deploy' - 'myapp-staging' - '--image' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$COMMIT_SHA' - '--region' - 'us-central1' - '--set-env-vars' - 'NODE_ENV=staging'
yaml# cloudbuild-production.yaml steps: - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' entrypoint: 'gcloud' args: - 'run' - 'deploy' - 'myapp-production' - '--image' - 'us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/myapp:$TAG_NAME' - '--region' - 'us-central1' - '--set-env-vars' - 'NODE_ENV=production'
Create triggers:
- Staging: Push to
developbranch →cloudbuild-staging.yaml - Production: Push tag
v*→cloudbuild-production.yaml
Static Site to Cloud Storage
yamlsteps: # Install and build - name: 'node:20' entrypoint: 'bash' args: - '-c' - | npm ci npm run build # Deploy to Cloud Storage - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' entrypoint: 'gsutil' args: - '-m' - 'rsync' - '-r' - '-d' - './dist' - 'gs://my-website-bucket' # Set cache headers - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk' entrypoint: 'gsutil' args: - '-m' - 'setmeta' - '-h' - 'Cache-Control:public, max-age=31536000' - 'gs://my-website-bucket/**/*.js'
Build Triggers for Different Workflows
Feature Branch Previews
bashgcloud builds triggers create github \ --repo-name=my-repo \ --repo-owner=my-user \ --branch-pattern="^feature/.*$" \ --build-config=cloudbuild-preview.yaml \ --name=preview-build
Pull Request Checks
bashgcloud builds triggers create github \ --repo-name=my-repo \ --repo-owner=my-user \ --pull-request-pattern="^main$" \ --build-config=cloudbuild-pr.yaml \ --name=pr-check \ --comment-control=COMMENTS_ENABLED_FOR_EXTERNAL_CONTRIBUTORS_ONLY
Tag-Based Production Deploys
bashgcloud builds triggers create github \ --repo-name=my-repo \ --repo-owner=my-user \ --tag-pattern="^v[0-9]+\.[0-9]+\.[0-9]+$" \ --build-config=cloudbuild-production.yaml \ --name=production-deploy
Notifications
Slack Notifications
- Create a Cloud Function to send Slack messages
- Set up Pub/Sub topic for build notifications
- Subscribe the function to the topic
bash# Create Pub/Sub topic gcloud pubsub topics create cloud-builds # Cloud Build automatically publishes to this topic
Email Notifications
- Go to Cloud Build → Settings
- Enable email notifications
- Configure recipients
Cost Optimization
Free Tier
- 120 build-minutes per day (free)
- E2_MEDIUM machine type
Reduce Build Time
- Use caching - Cache node_modules, build outputs
- Parallel steps - Run independent steps concurrently
- Smaller base images - Use Alpine or slim variants
- Multi-stage builds - Reduce final image size
Example with Caching
yamlsteps: # Restore cache - name: 'gcr.io/cloud-builders/gsutil' args: - '-m' - 'cp' - '-r' - 'gs://${PROJECT_ID}-build-cache/node_modules' - '.' allow_failure: true # Install only if cache miss - name: 'node:20' entrypoint: 'bash' args: - '-c' - | if [ ! -d "node_modules" ]; then npm ci fi # Save cache - name: 'gcr.io/cloud-builders/gsutil' args: - '-m' - 'cp' - '-r' - 'node_modules' - 'gs://${PROJECT_ID}-build-cache/'
Troubleshooting
Build Fails: Permission Denied
bash# Check Cloud Build service account permissions gcloud projects get-iam-policy $PROJECT_ID \ --flatten="bindings[].members" \ --filter="bindings.members:cloudbuild.gserviceaccount.com" # Grant missing role gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:${CLOUD_BUILD_SA}" \ --role="roles/MISSING_ROLE"
Build Fails: Docker Push Denied
bash# Grant Artifact Registry access gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:${CLOUD_BUILD_SA}" \ --role="roles/artifactregistry.writer"
Build Timeout
yaml# Increase timeout (max 24 hours) timeout: '3600s' options: machineType: 'E2_HIGHCPU_8' # Faster builds
View Detailed Logs
bash# Stream logs for running build gcloud builds log BUILD_ID --stream # Download logs gcloud builds log BUILD_ID > build.log
Note
Checkpoint: Create a cloudbuild.yaml file, connect your repository, and verify that pushing to your main branch triggers an automatic deployment.