Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive · GCP

GCP Services: Cloud Run and Artifact Registry

Build, scan and deploy containers with Cloud Build, Artifact Registry and Cloud Run, with private ingress, secrets, a database connection and gradual rollouts.

3 min read

  • Cloud Run
  • Artifact Registry
  • Cloud Build
  • Secret Manager
  • Cloud Load Balancing

Cloud Run runs containers without servers to manage: it scales from zero, bills for request time, and gives each service an HTTPS URL. This post builds the full path from source to a production service.

What changed recently. Google’s older Container Registry shut down on 18 March 2025, when writing images to it stopped; use Artifact Registry (gcr.io addresses that are hosted on Artifact Registry keep working). Cloud Functions 2nd generation was renamed Cloud Run functions in August 2024 and runs on the same platform as Cloud Run.

Architecture

flowchart TB
  DEV[Developer push] --> GIT[Git repository]
  GIT -->|trigger| CB[Cloud Build<br/>test and build image]
  CB --> AR[Artifact Registry<br/>private repository and scanning]
  AR --> CR[Cloud Run service<br/>new revision]
  U[Users] --> LB[External load balancer<br/>with Cloud Armor]
  LB -->|serverless NEG| CR
  CR -->|Secret Manager| SEC[Secrets]
  CR -->|Cloud SQL connector| SQL[Cloud SQL private IP]
  PS[Pub/Sub push subscription] -->|OIDC token| CR

Registry and image

gcloud services enable artifactregistry.googleapis.com run.googleapis.com cloudbuild.googleapis.com
gcloud artifacts repositories create apps --repository-format=docker --location=asia-south1 \
  --description="Application images"
gcloud auth configure-docker asia-south1-docker.pkg.dev

docker build -t asia-south1-docker.pkg.dev/demo-ci-2025/apps/shop-api:1.0 .
docker push asia-south1-docker.pkg.dev/demo-ci-2025/apps/shop-api:1.0

Add cleanup policies so old images do not accumulate (for example keep the last 10 versions and delete untagged images after 7 days), and rely on the Artifact Analysis vulnerability scanning that Artifact Registry offers for supported images. Deploy by image digest rather than a mutable tag where you can.

Deploy

gcloud run deploy shop-api \
  --image asia-south1-docker.pkg.dev/demo-ci-2025/apps/shop-api:1.0 \
  --region asia-south1 --service-account shop-api-sa@demo-ci-2025.iam.gserviceaccount.com \
  --min-instances 0 --max-instances 5 --concurrency 80 --cpu-boost \
  --set-secrets=DB_PASSWORD=shop-db-password:latest \
  --add-cloudsql-instances demo-ci-2025:asia-south1:shop-db \
  --network app-vpc --subnet app-mumbai --vpc-egress private-ranges-only \
  --ingress internal-and-cloud-load-balancing --no-allow-unauthenticated
Setting Why
Dedicated service account Least privilege; never use the default Compute Engine account
--min-instances 0 Scale to zero for cost; use 1 or more to avoid cold starts for latency-sensitive services
--concurrency 80 Requests handled by one instance at once (80 is the default); tune from load tests
--cpu-boost Extra CPU during start-up, shortening cold starts
--set-secrets Reads Secret Manager at runtime; grant roles/secretmanager.secretAccessor to the service account only
--ingress internal-and-cloud-load-balancing Blocks direct public access, so traffic must pass through the load balancer
--no-allow-unauthenticated Callers need roles/run.invoker, via IAM

Direct VPC egress (--network, --subnet, --vpc-egress) lets the service reach private resources, such as a Cloud SQL instance that has only a private IP, without a connector VM. Place the service behind an external Application Load Balancer using a serverless network endpoint group, then attach Cloud Armor and a managed certificate.

Build automation

A minimal cloudbuild.yaml run by a Cloud Build trigger on pushes to main:

steps:
  - name: gcr.io/cloud-builders/docker
    args: [build, -t, 'asia-south1-docker.pkg.dev/$PROJECT_ID/apps/shop-api:$SHORT_SHA', .]
  - name: gcr.io/cloud-builders/docker
    args: [push, 'asia-south1-docker.pkg.dev/$PROJECT_ID/apps/shop-api:$SHORT_SHA']
  - name: gcr.io/google.com/cloudsdktool/cloud-sdk
    entrypoint: gcloud
    args: [run, deploy, shop-api, --image, 'asia-south1-docker.pkg.dev/$PROJECT_ID/apps/shop-api:$SHORT_SHA',
           --region, asia-south1, --no-traffic, --tag, candidate]
options: { logging: CLOUD_LOGGING_ONLY }

(gcloud builds submit --tag ... is the one-off equivalent.)

Gradual rollout and rollback

Each deploy creates an immutable revision. Deploying with --no-traffic --tag candidate gives the new revision its own test URL; then shift traffic in steps:

gcloud run services update-traffic shop-api --region asia-south1 --to-tags candidate=10
# watch error rate and latency, then
gcloud run services update-traffic shop-api --region asia-south1 --to-latest
# roll back instantly
gcloud run services update-traffic shop-api --region asia-south1 --to-revisions shop-api-00012-abc=100

Operating it

  • Latency: cold starts matter for small services; use startup CPU boost, small images and minimum instances for user-facing paths.
  • Cost: you pay for request time (or for all instance time if CPU is always allocated); set maximum instances to cap runaway spend.
  • Events: Pub/Sub push subscriptions can call a private service using an OIDC token from a service account with roles/run.invoker.
  • Comparison with AWS: Cloud Run sits between Lambda and Fargate: you ship containers like Fargate, with Lambda-style scale to zero and per-request billing.