Google Cloud (GCP)
Containers that scale to zero, managed Postgres and a serverless data warehouse, all behind one IAM model.
In 30 seconds
Google Cloud is Google's public cloud: you rent compute, databases, storage and analytics instead of running your own servers. Its strongest pieces for web teams are Cloud Run (run any container, pay per request, scale to zero), Cloud SQL (managed Postgres or MySQL), GKE (managed Kubernetes) and BigQuery (a data warehouse you query with SQL without managing any servers). Everything lives in projects and is controlled by IAM, Google's permission system.
Not in my daily stack — I know the concepts and would learn the details when a project needs it.
Why it matters
- Cloud Run is one of the simplest ways to run a container in production: push an image, get an HTTPS URL, autoscaling and scale to zero, with no cluster to manage.
- BigQuery lets a team analyse billions of rows with plain SQL and pay per data scanned, which is why many companies keep their analytics on GCP even when the app runs elsewhere.
- GKE is widely seen as the most mature managed Kubernetes, and its Autopilot mode removes node management, so teams that already need Kubernetes often pick GCP.
- Compared with a single VPS, a team gets managed backups, failover, global load balancing and fine-grained IAM; the price is more concepts to learn and a bill that needs watching.
How it works
01The mental model: organisation, projects, regions
Everything in GCP belongs to a project. A project is a container for resources, billing and permissions: think of it as one apartment in a building, with its own keys and its own electricity meter. Projects can sit in folders under an organisation, which lets a company apply policies (for example "no public buckets") to every team at once. A common layout is one project per environment: app-dev, app-staging, app-prod. That gives you a hard wall between staging and production, separate quotas and a clear bill per environment. Resources run in a region (europe-west1, us-central1) and sometimes a zone inside it (europe-west1-b). Pick a region close to your users and keep the app and its database in the same one, because traffic across regions costs money and adds latency. Compared with AWS, a GCP project is roughly an AWS account, and a GCP organisation is roughly AWS Organizations.
- Organisation → folders → projects → resources.
- One project per environment is the simplest safe layout.
- Same region for app and database: lower latency, no inter-region egress.
02Compute: Cloud Run, GKE or Compute Engine
GCP gives you three main ways to run code. Compute Engine is plain virtual machines, the closest thing to a VPS: you manage the OS, patches and process manager yourself. GKE is managed Kubernetes. Standard mode gives you nodes to size and manage; Autopilot mode bills you for the CPU and memory your pods request and handles the nodes for you. Cloud Run sits on top: you hand it a container that listens on the PORT environment variable and it gives you an HTTPS endpoint, autoscaling by concurrent requests, revisions and traffic splitting. Cloud Run also runs jobs (batch tasks that run to completion), functions (the product formerly called Cloud Functions is now Cloud Run functions) and worker pools for pull-based background work. For a typical web API, start with Cloud Run. Move to GKE when you need many services with service mesh, custom networking or long-lived stateful workloads, and to Compute Engine when you need full control of the machine.
03Deploying a container to Cloud Run
The fastest path is to let Cloud Run build from source using Cloud Build and store the image in Artifact Registry (the old Container Registry has been shut down). For real projects, build the image in CI, push it to Artifact Registry, then deploy that exact image tag. Give the service its own service account with only the roles it needs, keep secrets in Secret Manager, and connect to Cloud SQL through the built-in connector. The key flags decide cost and behaviour: --min-instances keeps warm instances (no cold starts, but you pay for them), --max-instances caps both scale and your bill, and --concurrency sets how many requests one instance handles at once. Node and PHP apps usually handle many concurrent requests well, so the default of 80 is a sensible start.
gcloud config set project app-prod
gcloud services enable run.googleapis.com artifactregistry.googleapis.com
gcloud run deploy api \
--image=europe-west1-docker.pkg.dev/app-prod/apps/api:1.4.2 \
--region=europe-west1 \
[email protected] \
--set-secrets=DATABASE_URL=database-url:latest \
--add-cloudsql-instances=app-prod:europe-west1:main-db \
--min-instances=0 --max-instances=10 \
--concurrency=80 --cpu=1 --memory=512Mi \
--allow-unauthenticated
04Data: Cloud SQL, Firestore, Cloud Storage and BigQuery
Cloud SQL is managed PostgreSQL, MySQL or SQL Server: Google handles patching, automated backups, point-in-time recovery and optional high availability with a standby in another zone. It comes in Enterprise and Enterprise Plus editions; Plus adds faster failover and more performance at a higher price. AlloyDB is Google's Postgres-compatible engine for heavier workloads. Firestore is a serverless document database, popular with mobile apps. Cloud Storage holds files (uploads, backups, static assets) in buckets with storage classes from Standard to Archive. BigQuery is different from all of these: it is a columnar warehouse for analytics, not for your app's transactions. You load or stream events into it and run SQL over huge tables. On-demand pricing charges per terabyte scanned, so SELECT * on a wide table is literally expensive; select only the columns you need and partition tables by date.
- App data → Cloud SQL (or AlloyDB). Files → Cloud Storage.
- Analytics and reporting → BigQuery, fed from the app or by exports.
- Cloud SQL has no free tier: even the smallest instance bills every hour it exists.
05IAM: who can do what
IAM answers one question: which principal (a user, a group or a service account) has which role on which resource. Roles are bundles of permissions, such as roles/run.invoker or roles/cloudsql.client. Avoid the basic roles Owner, Editor and Viewer for anything but small experiments; they are far too broad. Give each workload its own service account and grant it predefined roles on the narrowest scope possible, a single bucket or secret rather than the whole project. The biggest security rule on GCP: do not create downloadable service account keys. A JSON key file is a long-lived password that ends up in repos and laptops. Code running on Google Cloud gets credentials automatically from the attached service account, and CI systems such as GitHub Actions should use Workload Identity Federation, which swaps a short-lived OIDC token for Google credentials. New organisations block key creation by policy by default for exactly this reason.
06Production tips and pricing traps
GCP bills by the second for most things, which is great until something runs that you forgot about. Set a budget with alerts on day one (budgets alert, they do not stop spending). The common surprises: Cloud SQL instances bill around the clock even when the app is idle; min-instances on Cloud Run bill while idle; Cloud NAT and load balancers have hourly charges; internet egress is charged per GB; and BigQuery on-demand queries are charged by bytes scanned, so a dashboard that refreshes a SELECT * every minute can cost more than the whole app. Logging is another trap: Cloud Logging charges for ingestion above the free allowance, so do not log full request bodies at debug level in production. The always-free tier covers small usage of Cloud Run, Cloud Storage, BigQuery and one small VM, and new accounts get trial credit, but read the limits before you rely on them.
Use it when
- You want to run containers without managing servers or a cluster, with scale to zero for spiky or low traffic: Cloud Run is excellent here.
- Your company does serious analytics or machine learning and wants BigQuery and Vertex AI close to the data.
- You need managed Kubernetes and want the least node management possible (GKE Autopilot).
- The team or client already uses Google Workspace and wants identity and billing in one place.
Skip it when
- A small app with steady traffic: one VPS with Docker Compose is usually cheaper and simpler to reason about.
- The client's company is fully committed to AWS or Microsoft; a second cloud doubles the IAM, networking and billing work.
- You need a service only another provider offers well, or strict data residency in a region GCP does not cover.
Alternatives
- AWS
The largest catalogue and market share. App Runner/ECS Fargate are the closest to Cloud Run; RDS to Cloud SQL; Redshift/Athena to BigQuery.
- Azure
The natural choice for Microsoft-centred companies: Entra ID, .NET, Office 365. Container Apps is its Cloud Run equivalent.
- VPS + Docker Compose
A fixed monthly price and full control, but you own backups, failover, patching and scaling yourself.
- Vercel / Netlify
Better for frontend-heavy Next.js or static sites; less suited to long-running backends and databases.
Best practices
- Use one project per environment and put them in a folder so policies apply to all of them.
- Give every service its own service account with predefined, narrowly scoped roles; never use Editor for workloads.
- Use Workload Identity Federation for CI/CD instead of service account JSON keys.
- Keep secrets in Secret Manager and mount them into Cloud Run, not in environment files or images.
- Set budgets with alerts at 50%, 90% and 100%, and set --max-instances on every Cloud Run service.
- Deploy immutable image tags from Artifact Registry and use Cloud Run revisions for instant rollback.
- Partition and cluster BigQuery tables and always select only the columns you need.
Common mistakes
- Downloading a service account key for local development and committing it to the repository.
- Leaving a Cloud SQL instance or a GKE Standard cluster running in a forgotten test project for months.
- Running background work after the HTTP response on Cloud Run with request-based billing, where CPU is throttled between requests.
- Putting the app in one region and the database in another, paying egress and latency on every query.
- Running SELECT * queries in BigQuery from dashboards, scanning terabytes for a few columns.
Deep dive
07Cloud Run internals: concurrency, CPU allocation and cold starts
Cloud Run scales on concurrent requests, not on CPU alone. If concurrency is 80 and 400 requests are in flight, it aims for about five instances. Each instance runs in a gVisor-based sandbox (second-generation execution environment gives full Linux compatibility and faster CPU and network). Billing has two modes. Request-based billing charges CPU and memory only while an instance is handling a request, and CPU is throttled close to zero between requests, so timers, queue consumers and fire-and-forget work after the response may simply stall. Instance-based billing (formerly "CPU always allocated") charges for the whole instance lifetime at a lower unit rate and lets background work run. Cold starts come from pulling the image and booting your runtime; reduce them with small images, lazy initialisation, startup CPU boost and, where latency matters, a minimum instance count of one or two.
# Background-friendly service: instance billing + warm instance
gcloud run services update worker-api \
--region=europe-west1 \
--no-cpu-throttling \
--cpu-boost \
--min-instances=1 \
--execution-environment=gen2
08Networking: VPCs, private access and egress
A GCP VPC is global: one network can have subnets in many regions, unlike AWS where a VPC is regional. Cloud Run is not inside your VPC by default; to reach private resources (Cloud SQL private IP, Memorystore Redis, internal VMs) use Direct VPC egress, which attaches the service to a subnet without the older Serverless VPC Access connector VMs. Outbound calls to the internet that must come from a fixed IP (partner allowlists) go through Cloud NAT with a reserved address. Private Service Connect and Private Google Access let workloads reach Google APIs without public IPs. For inbound traffic, a global external Application Load Balancer gives one anycast IP, managed certificates, Cloud CDN and Cloud Armor (WAF and rate limiting) in front of Cloud Run, GKE or buckets. Each of these pieces has its own hourly and per-GB charges, so draw the traffic path before you build it.
09Organisation policies, audit logs and guardrails
At company scale, the important controls sit above projects. Organisation policy constraints can forbid service account key creation, restrict resource locations to certain regions, block public IPs on VMs, enforce uniform bucket-level access and stop public sharing. Cloud Audit Logs record admin activity for free and can record data access (at extra cost) for compliance. Put sensitive projects behind VPC Service Controls to reduce data exfiltration risk: even a stolen credential cannot copy a BigQuery dataset to a project outside the perimeter. Use groups, not individual users, in IAM bindings, and review grants with IAM Recommender, which suggests removing permissions that were never used. Infrastructure should be in Terraform with remote state in a bucket, so every change is reviewed rather than clicked in the console.
Real-world scenarios
A Cloud Run API that keeps exhausting Postgres connections
Problem
During a traffic spike Cloud Run scaled to 60 instances and Cloud SQL started rejecting connections. Each instance opens a pool of 10 connections.
How to solve it
Do the maths first: 60 instances × 10 connections = 600, above the instance's max_connections. Cap scale with --max-instances sized to what the database can take, and shrink each pool to 2–5 connections, since one instance with concurrency 80 rarely needs more for short queries. If you need high scale, put a pooler between them (PgBouncer on a small VM, or Cloud SQL Managed Connection Pooling where available) and use transaction pooling. Add a connection timeout so requests fail fast instead of piling up. Finally, alert on Cloud SQL connection count and CPU, and consider a larger tier or read replicas for read-heavy endpoints.
CI needs to deploy without key files
Problem
The team's GitHub Actions pipeline uses a service account JSON key stored as a repository secret. Security wants it gone.
How to solve it
Create a Workload Identity Pool and an OIDC provider for GitHub, restricted with an attribute condition to your organisation and repository (for example assertion.repository == 'your_org/your_repo'). Create a deployer service account with only roles/run.developer, roles/artifactregistry.writer and roles/iam.serviceAccountUser on the runtime account. Allow the pool principal for that repository to impersonate it via roles/iam.workloadIdentityUser. In the workflow, use google-github-actions/auth with workload_identity_provider and service_account and permissions id-token: write. Delete the old key and enforce the organisation policy that blocks new keys.
The BigQuery bill jumps tenfold in a month
Problem
A new internal dashboard was launched and the BigQuery line on the bill grew from tens to hundreds of dollars.
How to solve it
Open INFORMATION_SCHEMA.JOBS and sort by total_bytes_billed to find the heavy queries; they usually come from one service account. Typical causes are SELECT * on a wide event table, no partition filter, and a refresh every minute. Fix the table: partition by event date, cluster by tenant or event type, and set require_partition_filter. Fix the queries: select needed columns and add date filters. Add a materialized view or a scheduled aggregation table for the dashboard so it reads megabytes, not terabytes. Then set maximum bytes billed and custom per-user quotas so a single query can never run away again.
Cheat sheet
- Log in and set a project
gcloud auth login && gcloud config set project app-prod- Local credentials for client libraries
gcloud auth application-default login- Deploy from source to Cloud Run
gcloud run deploy api --source . --region=europe-west1- Roll traffic back to a revision
gcloud run services update-traffic api --to-revisions=api-00042-abc=100- Read Cloud Run logs
gcloud run services logs read api --region=europe-west1 --limit=50- Create a secret
printf 'value' | gcloud secrets create database-url --data-file=-- Connect to Cloud SQL locally
cloud-sql-proxy app-prod:europe-west1:main-db --port 5432- Estimate a BigQuery query's cost
bq query --use_legacy_sql=false --dry_run 'SELECT col FROM ds.t WHERE dt = "2026-11-01"'- Grant a role on a project
gcloud projects add-iam-policy-binding app-prod --member=serviceAccount:[email protected] --role=roles/cloudsql.client
Questions & answers
Q1What is Cloud Run and when would you choose it over GKE?
Cloud Run is a serverless platform for containers. You give it an image that listens on a port and it handles HTTPS, autoscaling by concurrent requests, scaling to zero and rolling revisions. There are no nodes or clusters to manage, and with request-based billing you pay mostly while requests are being served. I choose it for stateless APIs, web apps, webhooks and scheduled jobs. GKE makes sense when you run many services that need Kubernetes features: custom networking, sidecars, service mesh, operators, stateful sets or fine control over scheduling. Many teams start on Cloud Run and only move to GKE when they hit a real limitation.
Q2How does authentication work for code running on Google Cloud?
Google's client libraries use Application Default Credentials. On Cloud Run, GKE or Compute Engine, the code automatically gets short-lived tokens for the service account attached to the workload from the metadata server, so no key file is needed. Locally, developers run gcloud auth application-default login to use their own identity. In CI, Workload Identity Federation lets GitHub Actions or GitLab exchange their OIDC token for Google credentials. The rule is: attach a dedicated service account with minimal roles and never ship JSON keys. Keys are long-lived, easy to leak and hard to rotate.
Q3How would you connect Cloud Run to a Cloud SQL database securely?
The simplest secure option is the built-in Cloud SQL connection: add the instance with --add-cloudsql-instances and connect through the Unix socket it provides, with the runtime service account holding roles/cloudsql.client. Traffic is encrypted and authorised by IAM without opening the database to the internet. For private networking, give the instance a private IP and use Direct VPC egress from Cloud Run. Store the database password in Secret Manager, or use IAM database authentication to avoid passwords entirely. Also cap connections: many Cloud Run instances with large pools can exhaust Postgres connection limits, so size pools small or add a pooler.
Q4What drives cost on BigQuery and how do you control it?
With on-demand pricing you pay per terabyte of data your queries scan, plus storage. Because BigQuery is columnar, the columns you select decide the bytes scanned, so SELECT * is the most expensive habit. Control cost by partitioning tables by date and requiring a partition filter, clustering by commonly filtered columns, selecting only needed columns, and checking the estimated bytes before running a query. Set a maximum bytes billed per query and custom quotas per user or project. For heavy, predictable use, capacity pricing with editions and reserved slots can be cheaper than on-demand.
Q5A client asks: is Google Cloud cheaper than a VPS?
It depends on the traffic shape. For a steady app that uses a small server all day, a VPS is usually cheaper and the bill is fixed. GCP becomes worth it when the managed services save real engineering time or risk: automatic backups and failover for the database, scaling for traffic spikes, scale to zero for low-traffic services, and IAM and audit logs for compliance. A small Cloud Run service can cost almost nothing, but a managed database, load balancer and egress add up. I would estimate both with the pricing calculator using real traffic numbers and include the cost of the work that a VPS leaves to the team.
Senior-level questions
S1Design a multi-region setup on GCP for a latency-sensitive API. What are the trade-offs?
Deploy the same Cloud Run service in two or three regions and put a global external Application Load Balancer in front with serverless network endpoint groups, so users hit the nearest healthy region on one anycast IP. Stateless compute is the easy part; data is the hard part. Options: a single primary Cloud SQL with cross-region read replicas (simple, writes still go to one region, failover means promoting a replica), Spanner for multi-region strong consistency (excellent but expensive and a different data model), or Firestore multi-region. Caching with Cloud CDN and Memorystore per region reduces cross-region reads. The trade-off is cost and complexity versus RTO and latency; many products are fine with one region, multi-zone HA and a tested restore plan in a second region.
S2Cloud Run versus GKE Autopilot for a platform of 30 microservices?
Cloud Run gives each team a near zero-ops deployment target, per-service scaling and scale to zero, and it integrates with IAM for service-to-service auth. Its limits show up with many services: no shared sidecars or mesh beyond what the platform offers, limited control over networking and scheduling, and per-service configuration sprawl unless you manage it with Terraform. GKE Autopilot gives the full Kubernetes API: Helm, operators, network policies, a service mesh, stateful workloads and portability across clouds, with Google managing nodes and billing by pod requests. The cost is Kubernetes knowledge, cluster upgrades and more moving parts. If most services are stateless HTTP APIs and the team is small, Cloud Run usually wins. If you need platform features or already run Kubernetes elsewhere, Autopilot.
S3How do you keep a GCP organisation secure as it grows from 3 to 50 engineers?
Move from people to structure. Use an organisation with folders per environment or business unit, and create projects from a Terraform module that applies labels, budgets, logging sinks and baseline IAM. Enforce organisation policies: block service account keys, restrict regions, enforce uniform bucket access and disable default network creation. Grant roles to Google groups, never to individuals, and use privileged access manager or just-in-time elevation for production admin rights. Centralise audit logs in a locked logging project. CI deploys through Workload Identity Federation with narrow deployer accounts. Add Security Command Center for misconfiguration findings and VPC Service Controls around sensitive data. Review unused permissions with IAM Recommender every quarter. The goal is that the secure path is also the default path.
Glossary
- Project
- The basic unit for resources, billing and IAM in Google Cloud; similar to an AWS account.
- Service account
- An identity for software rather than a person, attached to a workload so it can call Google APIs.
- Workload Identity Federation
- Lets external systems such as GitHub Actions exchange their own tokens for short-lived Google credentials, without key files.
- Cloud Run revision
- An immutable snapshot of a service's image and settings; traffic can be split or moved between revisions.
- Artifact Registry
- Google's registry for container images and language packages, replacing the retired Container Registry.
- Autopilot
- A GKE mode where Google manages nodes and you pay for the CPU and memory your pods request.
- Slot
- A unit of BigQuery compute capacity; reserved slots give predictable pricing instead of paying per byte scanned.
- Application Default Credentials
- The lookup order Google client libraries use to find credentials automatically in each environment.
Keep learning
Have a product in mind?Let's build it.
Tell me about your idea, your product or your team. I usually reply within one working day.