Google Cloud Platform Interview Questions and Answers
Last updated:
Check out 45 of the most common Google Cloud Platform interview questions, then take an AI-powered practice interview
Q1How does the GCP resource hierarchy work, and what happens to an IAM grant made at the organisation level?
BasicResource Hierarchy
Answer
GCP arranges everything into a strict tree: an Organization node at the top, then Folders (optional, nestable), then Projects, then the resources themselves such as a Compute Engine instance or a Cloud Storage bucket. The project is the real unit of work, it owns the billing account attachment, the enabled API list, quotas and the default network. Every project has an immutable globally unique project ID, a numeric project number that most APIs use internally, and a mutable display name.
Deleting a project puts it into a soft-delete state for roughly 30 days before resources are purged. IAM allow policies can be attached at any node and inherit downwards, so the effective policy on a resource is the union of every allow policy from the organisation down to that resource. This is exactly where interviewers push: allow policies are purely additive.
If a user holds roles/viewer at the organisation node, removing a binding on one project changes nothing at all. To actually block access you need an IAM deny policy attached higher up, an Organization Policy constraint, or a VPC Service Controls perimeter. Folders exist so the tree can mirror your business structure (environment, business unit, or both) and you can grant roles/compute.admin once on the non-prod folder instead of twenty separate times. The 2026 default recommendation is one workload per project per environment, folders for the boundaries you actually govern, and no human carrying roles/owner on a production project.
# Where am I in the tree?
gcloud projects describe my-prod-api --format='value(projectId,projectNumber,parent.id,parent.type)'
# Create a folder and a project underneath it
gcloud resource-manager folders create --display-name='prod' --organization=123456789012
gcloud projects create my-prod-api --folder=987654321098
gcloud beta billing projects link my-prod-api --billing-account=01ABCD-2345EF-67890A
# Grant once at the folder, inherited by every project inside
gcloud resource-manager folders add-iam-policy-binding 987654321098 \
--member='group:sre@example.com' \
--role='roles/compute.admin'
# See the effective grants a principal actually has on a project
gcloud projects get-iam-policy my-prod-api --flatten='bindings[].members' \
--filter='bindings.members:sre@example.com' --format='value(bindings.role)'
Key Points
- Organization > Folder > Project > Resource, with policies inheriting downward
- Project owns billing, quotas, enabled APIs; project ID is immutable
- Allow policies are additive, a child binding cannot revoke a parent grant
- Use IAM deny policies, Org Policy or VPC-SC to actually restrict
- Deleted projects sit in soft delete for about 30 days
Q2What is the difference between gcloud, gcloud storage, gsutil and bq, and how do gcloud configurations help?
BasicTooling
Answer
The Google Cloud CLI bundle ships several binaries. gcloud is the general control-plane tool with a consistent noun-verb grammar (gcloud compute instances list, gcloud run deploy). bq is the BigQuery client that predates gcloud and keeps its own flag style. gsutil is the legacy Cloud Storage tool, and it is deprecated: gcloud storage is the supported replacement and is meaningfully faster on large transfers because it parallelises and reuses connections by default. If you still write gsutil -m cp -r in an interview, expect a follow-up. Commands live in release tracks, gcloud, gcloud beta and gcloud alpha, and the beta or alpha surface often exposes flags the GA surface does not yet have.
Two flags matter constantly in scripting: --format (with value, json, csv or a projection expression) and --filter, which runs server side or client side depending on the API. Configurations are the piece candidates most often miss. gcloud config configurations create lets you keep named sets of project, region, zone and account, so switching between a dev project in asia-south1 and a prod project in asia-south2 is one command rather than four. Pair that with the CLOUDSDK_CORE_PROJECT environment variable in CI, and never rely on whatever project happened to be active on your laptop. For automation, always pin the project explicitly with --project rather than depending on ambient config, because a mis-set active project is the single most common cause of resources being created in the wrong place.
# Named configurations instead of re-running gcloud config set constantly
gcloud config configurations create prod
gcloud config set project my-prod-api
gcloud config set compute/region asia-south1
gcloud config set compute/zone asia-south1-a
gcloud config configurations activate prod
gcloud config configurations list
# gcloud storage replaces gsutil (deprecated)
gcloud storage cp -r ./build gs://my-static-site/
gcloud storage ls --long gs://my-static-site/
gcloud storage rsync ./dist gs://my-static-site --delete-unmatched-destination-objects
# Machine-readable output for scripts
gcloud compute instances list --project=my-prod-api \
--filter='status=RUNNING AND zone:asia-south1' \
--format='value(name,machineType.basename(),networkInterfaces[0].networkIP)'
# BigQuery keeps its own CLI
bq --location=asia-south1 query --use_legacy_sql=false 'SELECT CURRENT_DATE()'
Key Points
- gsutil is deprecated, gcloud storage is the supported and faster path
- bq is a separate CLI with its own flag conventions
- gcloud alpha and gcloud beta expose newer flags than the GA track
- --format and --filter make gcloud scriptable without jq gymnastics
- Named configurations avoid the classic wrong-project accident
Q3Explain basic, predefined and custom IAM roles. When would you actually create a custom role?
BasicIAM
Answer
IAM in GCP binds a principal (user, group, service account, or federated identity) to a role on a resource. Roles come in three flavours. Basic roles are the legacy trio roles/owner, roles/editor and roles/viewer, they span thousands of permissions across every service and should never appear on a production project. roles/editor in particular can modify IAM on many resources and deploy code, which makes it effectively owner in most threat models.
Predefined roles are service-scoped and curated by Google, for example roles/storage.objectViewer, roles/bigquery.dataEditor, roles/run.invoker, roles/secretmanager.secretAccessor. These are what you should reach for 90% of the time, and Google keeps them updated as new permissions appear in a service. Custom roles exist for the remaining cases: when no predefined role is tight enough and the extra permissions in the nearest predefined role are genuinely unacceptable, typically for auditors, for third-party vendor access, or for a service account that should be able to publish to Pub/Sub but never read a subscription.
Custom roles are defined at organisation or project level, carry a launch stage (ALPHA, BETA, GA, DEPRECATED), and are your responsibility to maintain: when Google adds a permission to a service, your custom role does not automatically get it, which produces mysterious permission-denied failures months later. Use the Policy Analyzer and the recommender (roles recommendations in IAM) to find over-granted bindings before an interviewer asks how you would tighten an existing project.
# Grant a tight predefined role instead of Editor
gcloud projects add-iam-policy-binding my-prod-api \
--member='serviceAccount:orders-api@my-prod-api.iam.gserviceaccount.com' \
--role='roles/secretmanager.secretAccessor' \
--condition='None'
# Custom role from a YAML definition
cat > publisher-only.yaml <<'EOF'
title: Pub/Sub Publisher Only
description: Publish messages, never read or manage subscriptions
stage: GA
includedPermissions:
- pubsub.topics.publish
- pubsub.topics.get
EOF
gcloud iam roles create pubsubPublisherOnly --project=my-prod-api --file=publisher-only.yaml
# Conditional binding: access only to buckets whose name starts with 'staging-'
gcloud projects add-iam-policy-binding my-prod-api \
--member='group:analysts@example.com' \
--role='roles/storage.objectViewer' \
--condition='title=staging-only,expression=resource.name.startsWith("projects/_/buckets/staging-")'
Key Points
- Basic roles (owner/editor/viewer) are legacy and far too broad for prod
- Predefined roles cover almost every real requirement
- Custom roles need manual maintenance as services add permissions
- IAM Conditions add resource, time or tag-based restrictions to a binding
- The IAM recommender surfaces over-granted bindings automatically
Q4What is a service account, and why is impersonation preferred over downloading a JSON key?
BasicIdentity
Answer
A service account is a non-human identity that both is a principal (you grant it roles) and is a resource (you grant humans roles on it). Every Compute Engine VM, GKE node, Cloud Run revision and Cloud Function runs as some service account, and if you do not specify one, the Compute Engine default service account is used, which historically was granted roles/editor on the project automatically. That default grant is the single most common privilege escalation path in an unhardened GCP org, and the constraint constraints/iam.automaticIamGrantsForDefaultServiceAccounts exists specifically to stop it.
Downloading a JSON key (gcloud iam service-accounts keys create) produces a long-lived credential with no expiry that ends up in .env files, CI variables, laptops and eventually a public repository. GCP's answer is impersonation: a human or a workload holds roles/iam.serviceAccountTokenCreator on the target service account and mints a short-lived OAuth token on demand, usually valid for an hour. Nothing durable is stored.
The gcloud flag is --impersonate-service-account, most client libraries support impersonated credentials natively, and Terraform supports it through the provider block. For workloads outside GCP, such as GitHub Actions, Workload Identity Federation removes keys entirely. The organisation policy constraints/iam.disableServiceAccountKeyCreation should be enforced at the org node in any serious environment. When an interviewer asks how you would rotate service account keys, the strongest answer is that you would delete them rather than rotate them, and describe the impersonation or federation path that replaces them.
# Allow a human group to impersonate a deploy service account
gcloud iam service-accounts add-iam-policy-binding \
deployer@my-prod-api.iam.gserviceaccount.com \
--member='group:platform@example.com' \
--role='roles/iam.serviceAccountTokenCreator'
# Run any gcloud command as that service account, no key file involved
gcloud storage ls gs://my-artifacts \
--impersonate-service-account=deployer@my-prod-api.iam.gserviceaccount.com
# Block key creation org-wide
gcloud resource-manager org-policies enable-enforce \
constraints/iam.disableServiceAccountKeyCreation --organization=123456789012
# Terraform provider using impersonation instead of a key
# provider "google" {
# project = "my-prod-api"
# region = "asia-south1"
# impersonate_service_account = "terraform@my-prod-api.iam.gserviceaccount.com"
# }
Key Points
- Service accounts are both principals and resources
- Default Compute Engine SA historically carried roles/editor, disable that grant
- Impersonation via roles/iam.serviceAccountTokenCreator issues one-hour tokens
- iam.serviceAccounts.actAs is required to deploy code as a service account
- Enforce constraints/iam.disableServiceAccountKeyCreation at the org node
Q5Walk through Cloud Storage classes, minimum storage durations and lifecycle rules.
BasicCloud Storage
Answer
Cloud Storage offers Standard, Nearline, Coldline and Archive. All four have identical latency and throughput characteristics, which surprises candidates coming from S3 Glacier: an Archive object is available in milliseconds, there is no restore job. What changes is the pricing shape.
Storage cost falls sharply as you go colder, while per-operation cost and a per-gigabyte retrieval fee rise. Each colder class also has a minimum storage duration: 30 days for Nearline, 90 days for Coldline, 365 days for Archive. If you delete, overwrite or transition an object before that window elapses you are charged an early-deletion fee for the remaining days, which is how teams accidentally make a cost-saving lifecycle rule more expensive than doing nothing.
Location matters independently of class: a bucket is regional (asia-south1), dual-region, or multi-region, and the multi-region and dual-region options cost more per gigabyte but give you cross-region durability and, for dual-region, turbo replication with a 15-minute RPO. Lifecycle management is configured per bucket as a set of rules with conditions (age, createdBefore, numNewerVersions, matchesStorageClass, daysSinceNoncurrentTime) and actions (SetStorageClass or Delete). Rules are evaluated asynchronously, typically once a day, so do not treat them as a real-time guarantee.
Autoclass is the alternative: enable it and Google moves objects between classes based on actual access patterns, charging a small management fee but removing the early-deletion risk entirely. Recent buckets also carry a soft delete policy that retains deleted objects for a default retention window, which shows up as unexpected storage cost if you churn objects heavily.
# Create a regional bucket in Mumbai with uniform bucket-level access
gcloud storage buckets create gs://my-app-media \
--location=asia-south1 --uniform-bucket-level-access --default-storage-class=STANDARD
# Lifecycle: cool after 30 days, archive after 180, delete old versions after 30
cat > lifecycle.json <<'EOF'
{
"lifecycle": {
"rule": [
{ "action": {"type": "SetStorageClass", "storageClass": "NEARLINE"},
"condition": {"age": 30, "matchesStorageClass": ["STANDARD"]} },
{ "action": {"type": "SetStorageClass", "storageClass": "ARCHIVE"},
"condition": {"age": 180, "matchesStorageClass": ["NEARLINE"]} },
{ "action": {"type": "Delete"},
"condition": {"daysSinceNoncurrentTime": 30} }
]
}
}
EOF
gcloud storage buckets update gs://my-app-media --lifecycle-file=lifecycle.json
# Or let Google decide, no early-deletion penalty
gcloud storage buckets update gs://my-app-media --enable-autoclass
Key Points
- All classes have millisecond access, only pricing differs
- Minimum durations: Nearline 30d, Coldline 90d, Archive 365d
- Early deletion fees can make an aggressive lifecycle rule cost more
- Autoclass automates transitions and avoids early-deletion charges
- Lifecycle rules run asynchronously, roughly daily, not instantly
Q6How do VPC networks, subnets and firewall rules work in GCP, and what are the implied rules?
BasicNetworking
Answer
A GCP VPC is a global resource, which is the biggest structural difference from AWS. One VPC spans every region, and subnets are regional with a primary IPv4 CIDR plus optional secondary ranges (used by GKE for pods and services). A VM in asia-south1 and a VM in europe-west1 on the same VPC talk over private IP with no peering, no transit gateway and no route table plumbing.
Auto-mode VPCs create a /20 subnet in every region automatically and are fine for demos but wrong for production, custom mode gives you deliberate CIDR planning, which matters because subnet primary ranges can be expanded but never shrunk or overlapped. Firewall rules are stateful, apply at the VM network interface, and are evaluated by priority from 0 to 65535 with lower numbers winning, default 1000. Targets are all instances, network tags, or service accounts, and targeting by service account is the more robust choice because tags can be attached by anyone who can edit an instance.
Every VPC carries four implied rules that cannot be deleted: implied allow egress to 0.0.0.0/0 and implied deny ingress from 0.0.0.0/0, both at priority 65535, plus the always-allowed paths to the metadata server at 169.254.169.254. Default networks additionally ship default-allow-ssh, default-allow-rdp, default-allow-icmp and default-allow-internal, and deleting those is usually step one of hardening. Hierarchical firewall policies at the folder or org level let you enforce a blanket deny that project owners cannot override.
# Custom-mode VPC with deliberate CIDRs and secondary ranges for GKE
gcloud compute networks create prod-vpc --subnet-mode=custom
gcloud compute networks subnets create prod-mum \
--network=prod-vpc --region=asia-south1 --range=10.20.0.0/20 \
--secondary-range=pods=10.60.0.0/14,services=10.64.0.0/20 \
--enable-private-ip-google-access --enable-flow-logs
# Allow the LB health checkers and the app port, scoped by service account
gcloud compute firewall-rules create allow-hc-to-api \
--network=prod-vpc --direction=INGRESS --priority=900 --action=ALLOW \
--rules=tcp:8080 \
--source-ranges=35.191.0.0/16,130.211.0.0/22 \
--target-service-accounts=api-node@my-prod-api.iam.gserviceaccount.com
# Explicit egress deny above the implied allow, then punch holes below it
gcloud compute firewall-rules create deny-all-egress \
--network=prod-vpc --direction=EGRESS --priority=65000 --action=DENY \
--rules=all --destination-ranges=0.0.0.0/0
Key Points
- VPC is global, subnets are regional, no peering needed across regions
- Implied rules: allow all egress, deny all ingress, both at priority 65535
- Lower priority number wins; 0 is the highest priority
- Target by service account rather than network tag for stronger control
- Subnet primary ranges expand but never shrink, plan CIDRs up front
Q7Compare Compute Engine machine families and explain when you would run Spot VMs.
BasicCompute Engine
Answer
Machine types are grouped into families. E2 is the cost-optimised general purpose family with no sustained-use discount but the lowest sticker price, good for dev and small services. N2, N2D and the newer N4 are balanced general purpose with committed-use discount support and custom machine type sizing.
C3, C3D and C4 are compute-optimised with higher per-core performance and Hyperdisk support, used for latency-sensitive serving. C4A runs on Google's own Axion Arm processors and gives strong price-performance if your container images are multi-arch. M-series are memory-optimised for SAP HANA and large in-memory databases, A-series and G-series carry GPUs for training and inference, and T2D is a cost-focused AMD option that punches above its price for scale-out web tiers.
GCP also lets you define custom machine types (an arbitrary vCPU and memory combination within family limits), which AWS does not, and this matters for right-sizing a workload that needs 6 vCPU and 40 GB rather than being pushed to the next standard size. Spot VMs are the preemptible successor: 60% to 91% cheaper, no maximum runtime (classic preemptible VMs were capped at 24 hours), and reclaimable at any time with a 30-second ACPI G2 shutdown signal delivered through the guest. Use them for batch, CI runners, Dataflow and Dataproc workers, GKE node pools serving stateless replicas, and rendering.
Do not use them for a stateful primary or anything where a 30-second drain is not enough. In production the pattern is a mixed managed instance group or two GKE node pools, one on-demand for baseline capacity and one Spot for burst.
# Custom machine type: 6 vCPU, 40 GB, exactly what the app needs
gcloud compute instances create api-1 \
--zone=asia-south1-a --custom-cpu=6 --custom-memory=40GB \
--image-family=debian-12 --image-project=debian-cloud \
--service-account=api-node@my-prod-api.iam.gserviceaccount.com \
--scopes=https://www.googleapis.com/auth/cloud-platform --no-address
# Spot VM with a graceful shutdown handler
gcloud compute instances create batch-worker-1 \
--zone=asia-south1-b --machine-type=c4-standard-8 \
--provisioning-model=SPOT --instance-termination-action=DELETE \
--metadata-from-file=shutdown-script=./drain.sh
# drain.sh reads the preemption flag from the metadata server
# curl -H 'Metadata-Flavor: Google' \
# http://metadata.google.internal/computeMetadata/v1/instance/preempted
Key Points
- E2 cheapest general purpose, N-series balanced, C-series compute-optimised
- C4A uses Axion Arm cores, needs multi-arch images
- Custom machine types avoid paying for unused vCPU or RAM
- Spot VMs save 60-91%, no 24-hour cap, 30-second preemption notice
- Mix on-demand baseline with Spot burst in a MIG or GKE node pool
Q8Which GCP resources are zonal, regional and global, and how does that change your availability design?
BasicRegions and Zones
Answer
Scope determines both the failure domain and the SLA. Zonal resources live in exactly one zone and die with it: Compute Engine VMs, zonal persistent disks and Hyperdisks, zonal GKE clusters, and zonal node pools. A single VM carries roughly a 99.5% availability SLA, so any design that keeps state on one VM is already below three nines.
Regional resources replicate across the zones in one region: subnets, regional managed instance groups, regional persistent disks (synchronous replication across two zones), regional GKE clusters with a replicated control plane, Cloud Run services, Cloud SQL with HA enabled, and regional Cloud Storage buckets. Global resources have no regional binding at all: VPC networks, firewall rules, routes, custom images, global external Application Load Balancers with a single anycast IP, and Cloud DNS. Multi-region resources are their own category, including multi-region Cloud Storage buckets, BigQuery datasets located in a multi-region such as asia, and Spanner multi-region configurations.
India has asia-south1 in Mumbai and asia-south2 in Delhi NCR, each with three zones, and both are the practical choice for latency-sensitive Indian consumer traffic and for data residency questions. The design rule interviewers want to hear: spread stateless compute across at least two zones behind a regional load balancer, use regional storage primitives for anything that must survive a zone outage, and treat cross-region as a deliberate disaster recovery decision with its own RPO and RTO rather than something you get for free. Also remember that a zone is not a data centre, it is a failure domain, and correlated failures across zones do occur.
Key Points
- Zonal: VMs, zonal disks, zonal GKE clusters, roughly 99.5% single-VM SLA
- Regional: subnets, regional MIGs, regional PDs, Cloud Run, Cloud SQL HA
- Global: VPC, firewall rules, images, global external Application LB, Cloud DNS
- asia-south1 (Mumbai) and asia-south2 (Delhi NCR) both have three zones
- Cross-region is a deliberate DR decision with explicit RPO and RTO
Q9How do you choose between Cloud Run, GKE, Cloud Run functions, App Engine and Compute Engine?
BasicCompute Selection
Answer
Start from the unit of deployment and the operational budget. Cloud Run is the 2026 default for stateless HTTP services and containers: you hand it an image that listens on the PORT environment variable, it scales from zero to thousands of instances, bills per 100 milliseconds of allocated CPU and memory, and requires no cluster. It handles concurrency inside an instance (default 80 concurrent requests), supports request timeouts up to 60 minutes, and has a jobs flavour for run-to-completion work.
Cloud Run functions, previously Cloud Functions, is the same underlying platform with a function-shaped source deployment and event triggers from Eventarc, useful for glue code and event handlers rather than full services. GKE is the answer when you need Kubernetes semantics: sidecars, DaemonSets, StatefulSets, custom operators, service meshes, GPU scheduling, or multi-tenant platform engineering. Autopilot mode removes node management and bills per pod resource request, Standard mode gives you node pools.
App Engine standard remains a reasonable choice for legacy Python, Java and Go apps already on it, but almost nobody starts new work there in 2026. Compute Engine is for anything Cloud Run and GKE cannot host: licensed third-party appliances, workloads that need a specific kernel or GPU driver stack, lift-and-shift migrations, and databases you insist on self-managing. The interview answer that scores is a rule of thumb with the trade-off attached: Cloud Run unless you need Kubernetes primitives, GKE when you do, Compute Engine only when the workload cannot be containerised, and always name cold starts, request timeout limits and background-work restrictions as the reasons Cloud Run might not fit.
Key Points
- Cloud Run: stateless containers, scale to zero, 60-minute max request timeout
- Cloud Run functions: event-driven glue via Eventarc triggers
- GKE: needed for sidecars, StatefulSets, operators, GPU scheduling, meshes
- Autopilot bills per pod request, Standard bills per node
- Compute Engine for non-containerisable or licence-bound workloads
Q10How does BigQuery bill a query, and what does 'on-demand versus editions' mean in practice?
BasicBigQuery
Answer
BigQuery separates storage from compute and bills them independently. Storage is charged per gigabyte per month with active storage rates for tables modified in the last 90 days and a roughly 50% cheaper long-term rate after that, applied automatically per partition. You can also opt a dataset into physical (compressed) storage billing, which is usually cheaper for well-compressed data but bills time travel and fail-safe bytes too.
Compute is where interviews focus. In on-demand mode you pay per tebibyte of data scanned, and because BigQuery is columnar, the bytes scanned depend on which columns you touch, not how many rows come back. SELECT * on a wide table is therefore expensive even with LIMIT 10, because LIMIT does not reduce bytes scanned.
A dry run tells you the cost before you execute. Editions (Standard, Enterprise and Enterprise Plus) are the capacity model that replaced legacy flat-rate pricing: you buy slots through a reservation, optionally with autoscaling between a baseline and a maximum, and queries in that reservation stop being billed by bytes. Editions make sense once your monthly on-demand spend is predictable and large, or when you need workload isolation so an analyst's ad-hoc scan cannot starve the hourly ETL.
A common production setup is one reservation with a small baseline plus autoscale for ELT jobs, and a separate assignment for BI traffic. Also know the free tier behaviour that catches people out: queries against metadata tables like INFORMATION_SCHEMA and cached results are free, but cache hits require byte-identical SQL and unchanged source tables.
-- Estimate cost before running anything
-- bq query --dry_run --use_legacy_sql=false 'SELECT ...'
-- Expensive: scans every column in the table
SELECT * FROM `analytics.events` WHERE event_date = '2026-08-01' LIMIT 10;
-- Cheap: two columns, one partition
SELECT user_id, event_name
FROM `analytics.events`
WHERE event_date = '2026-08-01';
-- Guard rail: fail the query instead of scanning a fortune
-- bq query --maximum_bytes_billed=10000000000 --use_legacy_sql=false '...'
-- Who spent what yesterday, from job metadata (free to query)
SELECT user_email,
COUNT(*) AS jobs,
ROUND(SUM(total_bytes_billed) / POW(1024, 4), 2) AS tib_billed
FROM `region-asia-south1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
AND job_type = 'QUERY' AND state = 'DONE'
GROUP BY user_email
ORDER BY tib_billed DESC;
Key Points
- On-demand bills bytes scanned, not rows returned; LIMIT does not help
- Columnar storage means column selection is the main cost lever
- Long-term storage rate kicks in per partition after 90 days without edits
- Editions buy slots with optional autoscaling and workload isolation
- maximum_bytes_billed is the cheapest guard rail against a runaway query
Q11How do you choose between Cloud SQL, AlloyDB, Spanner, Firestore, Bigtable and Memorystore?
BasicDatabases
Answer
Cloud SQL is managed MySQL, PostgreSQL and SQL Server. It is a single primary with optional read replicas and optional zonal failover, it scales vertically, and it is the correct default for any transactional workload that fits on one machine, which is most of them. AlloyDB is Google's PostgreSQL-compatible engine with a columnar accelerator for analytical queries on operational data, faster vacuum behaviour and better read scale-out, chosen when Cloud SQL for PostgreSQL runs out of headroom but you want to keep PostgreSQL wire compatibility.
Spanner is the horizontally scalable relational database with external consistency backed by TrueTime, synchronous replication and no failover window; it earns its price when you need global writes, unbounded scale or a strict five nines multi-region SLA, and it punishes you when you use it as a drop-in Postgres. Firestore in Native mode is a document database with realtime listeners and offline SDKs, the right pick for mobile and web app state; Datastore mode is the legacy server-side flavour. Bigtable is a wide-column store for very high write throughput with a single row key and no secondary indexes, used for time series, IoT telemetry, ad tech and personalisation feature stores.
Memorystore provides managed Redis and Valkey plus Memcached, used as a cache or session store, not as a source of truth. BigQuery deserves a mention as the analytical destination rather than a transactional store. The way to answer in an interview is to state the access pattern first (point lookups by key, relational joins with transactions, append-heavy time series, realtime sync to a mobile client) and let the service fall out of that, rather than reciting a feature list.
Key Points
- Cloud SQL: default OLTP, vertical scaling, read replicas, HA failover
- AlloyDB: PostgreSQL-compatible with columnar engine for mixed workloads
- Spanner: horizontal relational scale with TrueTime external consistency
- Bigtable: wide-column, single row key, no secondary indexes, huge writes
- Firestore for client-facing document state, Memorystore for caching only
Q12What are Application Default Credentials, and in what order does the client library look for them?
BasicAuthentication
Answer
Application Default Credentials (ADC) is the strategy every Google Cloud client library uses to find credentials without you writing auth code. The search order is fixed and worth memorising because it explains almost every 'works on my laptop, 403 in production' bug. First, the GOOGLE_APPLICATION_CREDENTIALS environment variable, if set, pointing at a service account key file or an external account credential configuration for Workload Identity Federation.
Second, the user credentials written by gcloud auth application-default login, stored under the gcloud config directory, which are a user identity and not a service account. Third, the attached service account via the metadata server, reachable at metadata.google.internal or 169.254.169.254 and requiring the Metadata-Flavor: Google header, which is how a Compute Engine VM, GKE pod, Cloud Run revision or Cloud Build step gets a token with no configuration whatsoever. If none of these resolve, the library raises a DefaultCredentialsError.
Two traps show up constantly. gcloud auth login authenticates the CLI only, it does not create ADC, so your Python or Node code will still fail until you run gcloud auth application-default login separately. And the quota project matters: ADC user credentials need a billing or quota project set, otherwise some APIs return a 'user project required' style error. On GCP, always prefer the metadata server path with a dedicated per-workload service account, and set scopes to cloud-platform on VMs while relying on IAM roles for the actual restriction, since legacy access scopes are a coarse second gate that only causes confusion.
# Local development: create ADC for your own identity
gcloud auth application-default login
gcloud auth application-default set-quota-project my-prod-api
# Local development impersonating the production runtime identity
gcloud auth application-default login \
--impersonate-service-account=orders-api@my-prod-api.iam.gserviceaccount.com
# What the metadata server returns inside a VM, GKE pod or Cloud Run container
curl -s -H 'Metadata-Flavor: Google' \
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email
# Node.js: no credentials in code, ADC resolves it
# import { Storage } from '@google-cloud/storage';
# const storage = new Storage(); // picks up ADC automatically
# const [files] = await storage.bucket('my-app-media').getFiles({ maxResults: 5 });
Key Points
- Order: GOOGLE_APPLICATION_CREDENTIALS, then gcloud ADC file, then metadata server
- gcloud auth login does not create ADC, application-default login does
- Metadata server needs the Metadata-Flavor: Google header
- Set a quota project for user-based ADC or some APIs reject the call
- Prefer per-workload service accounts over shared default identities
Q13How does Cloud Logging work, and how would you route only error logs to BigQuery while dropping noisy health checks?
BasicObservability
Answer
Everything written to Cloud Logging goes through the Log Router, which evaluates sinks against each incoming LogEntry. Every project has two default buckets: _Required, which keeps admin activity and system event audit logs for 400 days and cannot be modified or charged, and _Default, which keeps everything else for 30 days by default and is where ingestion cost accrues. Sinks have an inclusion filter and optional exclusion filters, and can target another log bucket, BigQuery, Cloud Storage, Pub/Sub, or a project in another organisation.
Ingestion into _Default is billed per gibibyte, so the highest-leverage cost action in most projects is an exclusion filter on load balancer health checks, Kubernetes liveness probes and readiness probe spam, which routinely account for the majority of log volume in a GKE cluster. Filters use the Logging query language: resource.type, severity, logName, jsonPayload field paths, and operators such as =~ for regular expressions. Log-based metrics turn a filter into a Cloud Monitoring counter or distribution metric that you can alert on, which is how you build an alert like 'more than 20 payment failures in five minutes' without shipping a custom metric from application code. Two production details interviewers check for: structured logging matters, because writing JSON to stdout from Cloud Run or GKE lets Logging parse fields into jsonPayload and makes filters possible at all, and severity must be set explicitly since plain stdout defaults to INFO while stderr maps to ERROR, which is why so many teams see their INFO logs classified as errors.
# Route only warnings and above from Cloud Run into BigQuery
gcloud logging sinks create run-errors-to-bq \
bigquery.googleapis.com/projects/my-prod-api/datasets/ops_logs \
--log-filter='resource.type="cloud_run_revision" AND severity>=WARNING' \
--use-partitioned-tables
# Grant the sink writer identity permission to write
gcloud projects add-iam-policy-binding my-prod-api \
--member="$(gcloud logging sinks describe run-errors-to-bq --format='value(writerIdentity)')" \
--role='roles/bigquery.dataEditor'
# Stop paying to ingest health checks into _Default
gcloud logging sinks update _Default \
--add-exclusion='name=drop-healthchecks,filter=httpRequest.requestUrl=~"/healthz|/readyz"'
# Log-based metric for alerting
gcloud logging metrics create payment_failures \
--description='Failed payment attempts' \
--log-filter='resource.type="cloud_run_revision" AND jsonPayload.event="payment_failed"'
Key Points
- _Required (400 days, free) and _Default (30 days, billed) buckets
- Sinks route to BigQuery, GCS, Pub/Sub or other log buckets
- Exclusion filters on health checks are the fastest logging cost win
- Log-based metrics convert a filter into an alertable Monitoring metric
- Write structured JSON to stdout so fields land in jsonPayload
Q14What disk options does Compute Engine offer, and how do snapshots and machine images differ?
BasicStorage
Answer
Block storage comes in three broad shapes. Persistent Disk is network-attached and survives instance deletion when you disable auto-delete, available as pd-standard (HDD, cheap and slow), pd-balanced (the sensible default), pd-ssd (higher IOPS per gigabyte) and pd-extreme (provisioned IOPS). A crucial characteristic is that PD throughput and IOPS scale with disk size and with the machine type's vCPU count, so a 20 GB pd-balanced attached to a large VM will bottleneck long before the CPU does, and the fix is often simply a bigger disk.
Hyperdisk is the newer generation on modern machine families where you provision capacity, IOPS and throughput independently, which removes the size-equals-speed coupling and is a common right-sizing lever. Local SSD is physically attached NVMe with very high IOPS and very low latency, but the data is lost on stop, on live migration failure and on instance deletion, so treat it strictly as scratch space for caches, temp files or shuffle. Filestore provides managed NFS when multiple VMs need a shared POSIX filesystem.
Snapshots are incremental at the block level and stored regionally or multi-regionally: the first snapshot is full, every later one stores only changed blocks, and deleting an intermediate snapshot does not break the chain because Google reassigns the blocks. Snapshot schedules automate retention. A machine image is a different object, it captures the full instance configuration (all attached disks, metadata, machine type, network config) as a single restorable unit, which is what you want for cloning an entire VM rather than just its data disk.
# Hyperdisk: provision capacity and performance separately
gcloud compute disks create db-data-1 --zone=asia-south1-a \
--type=hyperdisk-balanced --size=500GB \
--provisioned-iops=12000 --provisioned-throughput=600
# Automated snapshot schedule with retention and regional storage
gcloud compute resource-policies create snapshot-schedule daily-db \
--region=asia-south1 --max-retention-days=14 \
--daily-schedule --start-time=19:30 \
--storage-location=asia-south1 --on-source-disk-delete=apply-retention-policy
gcloud compute disks add-resource-policies db-data-1 \
--zone=asia-south1-a --resource-policies=daily-db
# Whole-instance capture, not just one disk
gcloud compute machine-images create api-golden-2026-08 \
--source-instance=api-1 --source-instance-zone=asia-south1-a
Key Points
- PD performance scales with disk size and vCPU count, undersized disks throttle
- Hyperdisk decouples capacity from provisioned IOPS and throughput
- Local SSD is ephemeral, lost on stop or delete, use for scratch only
- Snapshots are incremental and chain-safe when intermediates are deleted
- Machine images capture the whole instance, snapshots capture one disk
Q15Explain the GCP load balancer family and when you would pick a global external Application Load Balancer.
BasicLoad Balancing
Answer
Google's load balancers are classified along three axes: external versus internal, global versus regional, and Application (Layer 7) versus Network (Layer 4). The global external Application Load Balancer is the flagship: it uses a single anycast IPv4 or IPv6 address advertised from Google's edge worldwide, terminates TLS at the edge with managed certificates, supports URL maps for path and host routing, integrates with Cloud CDN, Cloud Armor and Identity-Aware Proxy, and can send traffic to backends in multiple regions with automatic failover based on capacity and health. This is what you put in front of a consumer product serving users across India and abroad.
The regional external Application Load Balancer keeps traffic and TLS termination inside one region, which matters for data residency requirements. Internal Application Load Balancers serve east-west HTTP traffic inside a VPC. The external passthrough Network Load Balancer operates at Layer 4, preserves the client source IP and does not proxy, which suits protocols other than HTTP and workloads that must see the real client address; the internal passthrough Network Load Balancer is the internal equivalent and is what Kubernetes Service type LoadBalancer creates by default in GKE.
Backend types include managed instance groups, network endpoint groups (zonal NEGs for GKE container-native load balancing, serverless NEGs for Cloud Run and App Engine, internet NEGs for external origins), and buckets for static content. Two details that come up: health checks originate from 35.191.0.0/16 and 130.211.0.0/22 and must be allowed through the firewall, and container-native load balancing through NEGs removes the extra kube-proxy hop that iptables-based routing adds.
# Serverless NEG in front of Cloud Run, behind a global external ALB
gcloud compute network-endpoint-groups create run-neg \
--region=asia-south1 --network-endpoint-type=serverless \
--cloud-run-service=orders-api
gcloud compute backend-services create orders-backend \
--global --load-balancing-scheme=EXTERNAL_MANAGED --enable-cdn
gcloud compute backend-services add-backend orders-backend \
--global --network-endpoint-group=run-neg --network-endpoint-group-region=asia-south1
gcloud compute url-maps create orders-lb --default-service=orders-backend
gcloud compute ssl-certificates create orders-cert \
--domains=api.example.in --global
gcloud compute target-https-proxies create orders-proxy \
--url-map=orders-lb --ssl-certificates=orders-cert
gcloud compute forwarding-rules create orders-fr \
--global --target-https-proxy=orders-proxy --ports=443 \
--load-balancing-scheme=EXTERNAL_MANAGED
Key Points
- Global external ALB uses one anycast IP with edge TLS termination
- Regional ALB keeps termination in-region for residency requirements
- Passthrough Network LB is L4, preserves client source IP, no proxying
- Serverless NEGs attach Cloud Run and App Engine to an ALB
- Allow health check ranges 35.191.0.0/16 and 130.211.0.0/22 in firewall rules
Q16What are the core Pub/Sub concepts, and what is the difference between pull and push subscriptions?
BasicPub/Sub
Answer
Pub/Sub is a globally available, horizontally scaling message bus. A publisher writes messages to a topic; each subscription attached to the topic gets its own independent copy of every message published after that subscription was created. That last clause is the classic bug: create the subscription before you publish, or the messages are simply gone.
Subscribers acknowledge messages, and unacknowledged messages are redelivered after the ack deadline expires (default 10 seconds, configurable up to 600, and client libraries usually extend it automatically through lease management while your handler runs). Messages are retained for a configurable period, up to 7 days by default and up to 31 days, and you can seek a subscription back to a timestamp or a snapshot to replay. Pull subscriptions have the subscriber calling the API, either with the streaming pull used by the client libraries or unary pull; this gives you flow control, batching, and back-pressure, which is why almost every high-throughput consumer uses pull.
Push subscriptions have Pub/Sub POST the message to an HTTPS endpoint, and the HTTP response code is the acknowledgement: 2xx acks, anything else is a nack that triggers exponential backoff. Push is the natural fit for Cloud Run because it lets the service scale from zero on delivery, and you should always configure an OIDC token on the push subscription so the receiver can verify the caller rather than accepting anonymous POSTs. Also know that BigQuery and Cloud Storage subscriptions exist and write directly to those sinks with no subscriber code at all.
# Topic and a pull subscription with a longer ack deadline
gcloud pubsub topics create order-events
gcloud pubsub subscriptions create order-events-worker \
--topic=order-events --ack-deadline=60 \
--message-retention-duration=7d --enable-exactly-once-delivery
# Push subscription into Cloud Run, authenticated with an OIDC token
gcloud pubsub subscriptions create order-events-run \
--topic=order-events \
--push-endpoint=https://orders-api-xyz.asia-south1.run.app/pubsub \
--push-auth-service-account=pubsub-invoker@my-prod-api.iam.gserviceaccount.com \
--min-retry-delay=10s --max-retry-delay=600s
# Replay: rewind the subscription to a point in time
gcloud pubsub subscriptions seek order-events-worker \
--time=2026-08-10T04:00:00Z
# No-code sink straight into BigQuery
gcloud pubsub subscriptions create order-events-bq \
--topic=order-events \
--bigquery-table=my-prod-api:ops_logs.order_events --use-table-schema
Key Points
- Subscriptions only receive messages published after they exist
- Ack deadline defaults to 10s, extended automatically by client libraries
- Pull gives flow control and batching; push scales Cloud Run from zero
- Always attach an OIDC service account to push endpoints
- Seek and snapshots enable replay within the retention window
Q17How do Artifact Registry and Cloud Build fit into a GCP deployment pipeline, and what replaced Container Registry?
BasicCI/CD
Answer
Container Registry (the gcr.io hostnames) has been deprecated and shut down, and Artifact Registry is the supported replacement. Artifact Registry is multi-format: Docker and OCI images, Maven, npm, Python, Go modules, Debian and RPM packages, plus remote repositories that proxy and cache upstream registries like Docker Hub (useful because it insulates your builds from Docker Hub rate limits) and virtual repositories that present several backing repos behind one URL. Repositories are regional or multi-regional, so create yours in asia-south1 if your GKE cluster or Cloud Run service is in Mumbai, otherwise every image pull crosses regions and you pay egress and latency for it.
Hostnames look like asia-south1-docker.pkg.dev/PROJECT/REPO/IMAGE:TAG, and you authenticate Docker to them with gcloud auth configure-docker. Cloud Build is the managed build service: it reads a cloudbuild.yaml (or a Dockerfile directly), runs each step as a container on a shared or private worker pool, and supports substitutions, secrets from Secret Manager, and triggers on GitHub, GitLab or Cloud Source Repositories pushes. Two operational details matter.
Builds run as a service account, and Google changed the default for newer projects away from the legacy Cloud Build service account, so a build that works in an old project can fail with permission errors in a new one until you grant the right roles explicitly. Second, enable vulnerability scanning on the repository and use Binary Authorization if your interviewer asks about supply chain security: it enforces at admission time that only attested images from your pipeline can run on GKE or Cloud Run.
# Regional Docker repository plus vulnerability scanning
gcloud artifacts repositories create app \
--repository-format=docker --location=asia-south1 \
--description='Application images'
gcloud auth configure-docker asia-south1-docker.pkg.dev
# cloudbuild.yaml
steps:
- name: gcr.io/cloud-builders/docker
args: ['build', '-t', '$_IMAGE:$SHORT_SHA', '-t', '$_IMAGE:latest', '.']
- name: gcr.io/cloud-builders/docker
args: ['push', '--all-tags', '$_IMAGE']
- name: gcr.io/google.com/cloudsdktool/cloud-sdk
entrypoint: gcloud
args: ['run', 'deploy', 'orders-api', '--image=$_IMAGE:$SHORT_SHA',
'--region=asia-south1', '--no-traffic', '--tag=candidate']
substitutions:
_IMAGE: asia-south1-docker.pkg.dev/my-prod-api/app/orders-api
options:
logging: CLOUD_LOGGING_ONLY
machineType: E2_HIGHCPU_8
Key Points
- gcr.io Container Registry is gone, Artifact Registry is the successor
- Remote repositories cache Docker Hub and dodge upstream rate limits
- Co-locate the repository with the runtime region to avoid egress
- Cloud Build runs steps as containers with Secret Manager integration
- Binary Authorization enforces attested-only deploys to GKE and Cloud Run
Q18How do you attribute and control cost in GCP using labels, billing export and budgets?
BasicCost Management
Answer
Cost attribution in GCP rests on three mechanisms that candidates often confuse. Labels are key-value pairs attached to resources (VMs, disks, buckets, BigQuery datasets, Cloud Run services) that flow into billing data, and they are the primary way to answer 'what did the payments team spend last month'. Network tags are a completely different thing used only for firewall targeting and routing, they never appear in billing.
Resource Manager tags are a third concept, inheritable through the hierarchy and usable in IAM conditions and Organization Policy, and they also appear in billing exports. The practical rule is to enforce a small mandatory label set (env, team, service, cost-centre) through Terraform defaults rather than hoping people remember. Billing export writes detailed usage records into BigQuery, and this is the only way to do serious analysis: the console dashboards are fine for eyeballing but cannot answer questions like 'which single Cloud Run revision drove the spike on 3 August'.
Enable the detailed export, which includes resource-level granularity, not just the standard one. Budgets are set on a billing account or filtered to specific projects, services or labels, and they fire Pub/Sub notifications at threshold percentages of actual or forecast spend. Budgets do not stop spending, and a good answer says so explicitly, the only way to hard-stop is a Cloud Function subscribed to the budget topic that detaches the billing account, which is a blunt instrument suitable for sandbox projects only. Pair budgets with quota limits and with the Recommender API, which surfaces idle VMs, unattached disks and oversized instances.
-- Detailed billing export: top spending services this month
SELECT service.description AS service,
(SELECT value FROM UNNEST(labels) WHERE key = 'team') AS team,
ROUND(SUM(cost), 2) AS cost_inr,
ROUND(SUM(IFNULL((SELECT SUM(c.amount) FROM UNNEST(credits) c), 0)), 2) AS credits
FROM `my-billing.billing_export.gcp_billing_export_resource_v1_01ABCD_2345EF_67890A`
WHERE DATE(usage_start_time) >= DATE_TRUNC(CURRENT_DATE(), MONTH)
AND project.id = 'my-prod-api'
GROUP BY service, team
ORDER BY cost_inr DESC
LIMIT 20;
-- Budget with Pub/Sub alerts at 50/90/100 percent of forecast
-- gcloud billing budgets create --billing-account=01ABCD-2345EF-67890A \
-- --display-name='prod-monthly' --budget-amount=800000INR \
-- --threshold-rule=percent=0.5 --threshold-rule=percent=0.9 \
-- --threshold-rule=percent=1.0,basis=forecasted-spend \
-- --filter-projects=projects/123456789012
Key Points
- Labels appear in billing, network tags do not, they are unrelated concepts
- Enable the detailed billing export for resource-level attribution
- Budgets alert only, they never stop spend on their own
- Recommender surfaces idle VMs, unattached disks and oversized machines
- Enforce a mandatory label set through Terraform rather than by policy memo
Q19How does Workload Identity Federation let GitHub Actions deploy to GCP without a service account key?
IntermediateIdentity Federation
Answer
Workload Identity Federation lets an external identity provider that issues OIDC or SAML tokens exchange those tokens for short-lived Google access tokens, so no long-lived JSON key ever leaves GCP. You create a workload identity pool, then a provider inside it that points at the issuer, for GitHub Actions that is https://token.actions.githubusercontent.com. The provider declares attribute mappings that translate claims in the incoming token into Google attributes: google.subject from assertion.sub, attribute.repository from assertion.repository, attribute.ref from assertion.ref.
Then you grant roles/iam.workloadIdentityUser on the target service account to a principalSet scoped by those attributes, so only workflows from your repository can impersonate it. The single most important control is the attribute condition on the provider. Without a condition such as assertion.repository_owner == 'my-org', any GitHub repository in the world presenting a valid GitHub OIDC token could attempt the exchange, and the binding becomes the only thing standing between you and a stranger's workflow.
Interviewers specifically check whether you remember this. In the workflow itself you request the OIDC token by setting permissions.id-token: write, then use google-github-actions/auth with the provider resource name and the service account email; the action writes an external account credential file and exports GOOGLE_APPLICATION_CREDENTIALS so every subsequent gcloud or client library call resolves ADC through the federation path. The same mechanism covers AWS workloads, Azure, Okta, Kubernetes clusters outside GCP, and CI systems like GitLab, which is why the org policy disabling service account key creation is realistically enforceable in 2026.
# 1. Pool and provider, with a mandatory attribute condition
gcloud iam workload-identity-pools create github --location=global
gcloud iam workload-identity-pools providers create-oidc gh-actions \
--location=global --workload-identity-pool=github \
--issuer-uri='https://token.actions.githubusercontent.com' \
--attribute-mapping='google.subject=assertion.sub,attribute.repository=assertion.repository' \
--attribute-condition="assertion.repository_owner == 'my-org'"
# 2. Let only one repo impersonate the deployer service account
gcloud iam service-accounts add-iam-policy-binding \
deployer@my-prod-api.iam.gserviceaccount.com \
--role='roles/iam.workloadIdentityUser' \
--member='principalSet://iam.googleapis.com/projects/123456789012/locations/global/workloadIdentityPools/github/attribute.repository/my-org/orders-api'
# 3. .github/workflows/deploy.yml
# permissions:
# id-token: write
# contents: read
# steps:
# - uses: google-github-actions/auth@v2
# with:
# workload_identity_provider: projects/123456789012/locations/global/workloadIdentityPools/github/providers/gh-actions
# service_account: deployer@my-prod-api.iam.gserviceaccount.com
Key Points
- Exchanges an external OIDC token for a short-lived Google access token
- Attribute mappings turn issuer claims into principalSet identifiers
- An attribute condition on the provider is mandatory, not optional
- Grant roles/iam.workloadIdentityUser scoped to repository or branch
- Removes the last real reason to create service account JSON keys
Q20How does Workload Identity Federation for GKE work, and what breaks when it is misconfigured?
IntermediateGKE Security
Answer
Before Workload Identity, every pod on a node inherited the node's service account through the metadata server, meaning a compromised pod got whatever the node pool could do, often roles/editor via the default Compute Engine service account. Workload Identity Federation for GKE fixes that by mapping a Kubernetes service account to a Google service account. You enable it on the cluster with a workload pool of the form PROJECT_ID.svc.id.goog, enable the GKE metadata server on each node pool, annotate the Kubernetes service account with iam.gke.io/gcp-service-account, and grant roles/iam.workloadIdentityUser on the Google service account to the member serviceAccount:PROJECT.svc.id.goog[NAMESPACE/KSA_NAME].
Pods then get tokens for the mapped Google identity automatically through ADC with no key file. Recent GKE versions also support granting IAM directly to the Kubernetes service account principal without an intermediate Google service account, which removes a whole layer of wiring. The failure modes are distinctive and worth naming.
If the node pool does not have the GKE metadata server enabled, pods silently keep using the node identity, so everything works but with the wrong, over-privileged principal, which is the dangerous case. If the annotation is missing or misspelled, calls fail with a 403 mentioning the node service account rather than the one you expected. If the workloadIdentityUser binding is missing, the token exchange fails and client libraries report that the caller cannot impersonate.
Because the binding string embeds the namespace, moving a deployment to a different namespace breaks authentication in a way that looks like an application bug. Always confirm the effective identity from inside the pod before debugging application code.
# Cluster and node pool must both be enabled
gcloud container clusters update prod-mum \
--region=asia-south1 --workload-pool=my-prod-api.svc.id.goog
gcloud container node-pools update default-pool \
--cluster=prod-mum --region=asia-south1 --workload-metadata=GKE_METADATA
# Bind the Kubernetes SA to the Google SA
gcloud iam service-accounts add-iam-policy-binding \
orders-api@my-prod-api.iam.gserviceaccount.com \
--role='roles/iam.workloadIdentityUser' \
--member='serviceAccount:my-prod-api.svc.id.goog[orders/orders-api]'
# k8s ServiceAccount
# apiVersion: v1
# kind: ServiceAccount
# metadata:
# name: orders-api
# namespace: orders
# annotations:
# iam.gke.io/gcp-service-account: orders-api@my-prod-api.iam.gserviceaccount.com
# Verify from inside the pod which identity is actually in use
kubectl -n orders exec -it deploy/orders-api -- \
curl -s -H 'Metadata-Flavor: Google' \
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email
Key Points
- Maps a Kubernetes SA to a Google SA, removing node-identity inheritance
- Needs cluster workload pool plus GKE_METADATA on every node pool
- Binding member format is PROJECT.svc.id.goog[namespace/ksa]
- Missing GKE metadata server silently falls back to the node identity
- Changing the pod namespace invalidates the binding
Q21GKE Autopilot versus Standard: what actually changes, and what can you no longer do in Autopilot?
IntermediateGKE
Answer
In Standard mode you own node pools: machine type, disk, autoscaling bounds, taints, node images, and the cost of every node whether pods occupy it or not. In Autopilot mode Google owns the nodes entirely. You submit pods, GKE provisions capacity to fit them, and you are billed for the CPU, memory and ephemeral storage your pods request rather than for node capacity.
That flips the optimisation problem: in Standard you fight bin-packing and idle nodes, in Autopilot you fight over-requesting, because a deployment that requests 4 vCPU and uses 0.4 is now directly a bill. Autopilot enforces guardrails: it applies minimum and maximum resource requests, rounds requests to allowed increments, sets limits equal to requests for CPU and memory in the default compute class, blocks privileged containers, blocks hostPath and host namespaces, restricts most DaemonSets that need node-level access, and does not allow SSH to nodes or arbitrary node modifications. That last set is exactly what interviewers probe, because it rules out many agent-based monitoring and security tools, some CNI plugins, and any workload that needs a custom kernel module.
Autopilot supports compute classes (such as Balanced, Scale-Out, Performance and Accelerator classes for GPUs) and Spot pods, so it is not restricted to one hardware profile. Standard remains the right answer when you need node-level control, an unusual node OS, node-local caching on Local SSD, or when a very steady, densely packed workload makes per-node billing cheaper than per-pod billing. For most teams that just want Kubernetes without an SRE dedicated to node upgrades, Autopilot is the 2026 default recommendation.
# Autopilot cluster in Mumbai, private nodes
gcloud container clusters create-auto prod-mum \
--region=asia-south1 --release-channel=regular \
--enable-private-nodes --workload-pool=my-prod-api.svc.id.goog
# Autopilot bills what you request, so requests must be honest
# apiVersion: apps/v1
# kind: Deployment
# spec:
# template:
# spec:
# nodeSelector:
# cloud.google.com/compute-class: Balanced
# containers:
# - name: api
# image: asia-south1-docker.pkg.dev/my-prod-api/app/orders-api:1.4.2
# resources:
# requests:
# cpu: "500m"
# memory: "512Mi"
# Standard mode when you need node control
gcloud container node-pools create spot-pool --cluster=prod-mum \
--region=asia-south1 --spot --machine-type=c4-standard-8 \
--enable-autoscaling --min-nodes=0 --max-nodes=20 \
--node-taints=workload=batch:NoSchedule
Key Points
- Autopilot bills pod requests, Standard bills node capacity
- Autopilot blocks privileged pods, hostPath, host namespaces and node SSH
- Compute classes and Spot pods are available in Autopilot
- Over-requesting resources is the main Autopilot cost trap
- Standard is required for node-level agents, custom OS and Local SSD
Q22Explain the Cloud Run concurrency and CPU allocation model. How do you tune it for a Node.js API?
IntermediateCloud Run
Answer
Cloud Run scales on two dimensions at once, and understanding the interaction is what separates people who have run it in production from people who have read the docs. Each instance handles up to a configured number of concurrent requests, default 80 and maximum 1000. The autoscaler adds instances when the observed concurrency or CPU utilisation exceeds the target, and removes them after a period of idleness, down to the min-instances floor (zero by default).
Total capacity is therefore max-instances times concurrency, and your real ceiling is usually whichever downstream resource saturates first, most often the database connection pool. This is the classic Cloud Run outage in India and everywhere else: 100 instances times a pool of 10 connections is 1000 connections against a Cloud SQL instance that allows 200, and everything fails at once during a traffic spike. Concurrency tuning depends on the runtime.
A Node.js or Python async service that spends most of its time waiting on I/O handles high concurrency well, so 80 or higher is right. A CPU-bound service, or one holding significant per-request memory, should drop concurrency to a handful so each instance is not thrashing. CPU allocation is the second lever: by default CPU is throttled outside a request, meaning background timers, async flushes and queue consumers stop between requests, which silently breaks OpenTelemetry batch exporters and fire-and-forget writes.
Setting CPU always allocated keeps the process running between requests and is required for background work, and it changes billing to instance-time rather than request-time. Startup CPU boost gives extra CPU during cold start, which materially cuts p99 latency for JVM and Node apps with heavy initialisation.
gcloud run deploy orders-api \
--image=asia-south1-docker.pkg.dev/my-prod-api/app/orders-api:1.4.2 \
--region=asia-south1 \
--concurrency=80 \
--min-instances=2 \
--max-instances=50 \
--cpu=2 --memory=1Gi \
--cpu-boost \
--no-cpu-throttling \
--timeout=120 \
--service-account=orders-api@my-prod-api.iam.gserviceaccount.com \
--set-secrets=DB_PASSWORD=db-password:latest \
--no-allow-unauthenticated
// Size the pool from the container's own concurrency, not a copied constant
// const pool = new Pool({
// max: Number(process.env.PG_POOL_MAX ?? 5), // 50 instances x 5 = 250 conns
// idleTimeoutMillis: 10_000,
// connectionTimeoutMillis: 3_000,
// });
// process.on('SIGTERM', async () => { await pool.end(); process.exit(0); });
Key Points
- Concurrency default 80, max 1000; capacity is max-instances times concurrency
- Max instances times pool size must stay under the database connection limit
- CPU is throttled between requests unless you set --no-cpu-throttling
- --cpu-boost cuts cold-start latency for JVM and Node initialisation
- min-instances removes cold starts at the cost of always-on billing
Q23What is the Cloud Run container contract, and how do revisions and traffic splitting work?
IntermediateCloud Run
Answer
The container contract is short but strictly enforced. Your process must listen on 0.0.0.0 at the port given by the PORT environment variable (8080 by default), must start listening within the startup timeout, must be stateless because the filesystem is an in-memory tmpfs that counts against your memory limit, and must handle SIGTERM for graceful shutdown. The most common deployment failure message is 'Container failed to start and listen on the port defined by the PORT environment variable', and it almost always means one of four things: the app hard-codes 3000 or binds to 127.0.0.1 instead of 0.0.0.0, the image is built for the wrong CPU architecture (an Arm image from an Apple Silicon laptop pushed without --platform linux/amd64), a missing environment variable causes the process to exit during boot, or initialisation genuinely takes longer than the startup probe allows.
Every deploy creates an immutable revision. Traffic is assigned to revisions by percentage, so a canary is a single command, and each revision can carry a tag that gives it its own stable URL for testing without receiving any production traffic. That combination, deploying with --no-traffic --tag=candidate, smoke-testing the tagged URL, then shifting 5% and watching error rate, is the standard safe-release pattern and a very common interview answer.
Rollback is instantaneous because the previous revision still exists: you just move traffic back. Note that changing environment variables, secrets, concurrency, CPU or memory all create new revisions too, which is why configuration drift in Cloud Run is far less painful than on a mutable VM fleet.
# Deploy without taking traffic, then test the tagged URL
gcloud run deploy orders-api --region=asia-south1 \
--image=asia-south1-docker.pkg.dev/my-prod-api/app/orders-api:1.5.0 \
--no-traffic --tag=candidate
curl -s https://candidate---orders-api-xyz.asia-south1.run.app/healthz
# Canary 5 percent, then promote, then roll back if needed
gcloud run services update-traffic orders-api --region=asia-south1 \
--to-tags=candidate=5
gcloud run services update-traffic orders-api --region=asia-south1 \
--to-latest
gcloud run services update-traffic orders-api --region=asia-south1 \
--to-revisions=orders-api-00041-abc=100
// The listener the contract requires
// const port = Number(process.env.PORT) || 8080;
// app.listen(port, '0.0.0.0', () => console.log('listening on ' + port));
# Build for the right architecture from an Apple Silicon machine
docker build --platform linux/amd64 -t asia-south1-docker.pkg.dev/my-prod-api/app/orders-api:1.5.0 .
Key Points
- Bind 0.0.0.0 on process.env.PORT, never a hard-coded port or localhost
- Filesystem is in-memory tmpfs and counts against the memory limit
- Every deploy or config change produces an immutable revision
- Tags give a revision a stable URL with zero production traffic
- Arm images from Apple Silicon are a frequent cause of start failures
Q24When do you partition a BigQuery table versus cluster it, and what does require_partition_filter do?
IntermediateBigQuery
Answer
Partitioning physically splits a table into segments, one per time unit or integer range, and lets the query engine skip entire segments. You can partition by ingestion time (_PARTITIONTIME), by a DATE, DATETIME or TIMESTAMP column with hourly, daily, monthly or yearly granularity, or by an integer range. A partitioned table supports up to a large but finite number of partitions (in the tens of thousands), which is why hourly partitioning on multi-year data eventually hits the limit and daily is the safer default.
Pruning only happens when the filter is a constant expression the planner can evaluate before execution: WHERE event_date = CURRENT_DATE() prunes, but joining to a subquery that produces the date does not, and neither does wrapping the partition column in a function. Clustering sorts data within each partition by up to four columns in the order you declare them, storing block-level min and max values so BigQuery can skip blocks. Clustering shines on high-cardinality filter and join columns such as user_id, merchant_id or country, and the column order matters because filtering on the second clustering column alone gives much weaker pruning than filtering on the first.
Unlike partitioning, clustering cost savings are not visible in the dry-run estimate, the estimate shows the worst case and the actual bytes billed come out lower. require_partition_filter is the guard rail: set it on the table and any query that omits a partition filter is rejected outright with an error rather than quietly scanning years of data. On a large event table in a shared analytics project this single setting prevents more cost incidents than any dashboard.
CREATE TABLE `analytics.events`
(
event_date DATE NOT NULL,
event_ts TIMESTAMP NOT NULL,
user_id STRING NOT NULL,
merchant_id STRING,
event_name STRING,
payload JSON
)
PARTITION BY event_date
CLUSTER BY merchant_id, user_id
OPTIONS (
require_partition_filter = TRUE,
partition_expiration_days = 400,
description = 'Raw product events, Mumbai region'
);
-- Prunes: literal predicate on the partition column
SELECT COUNT(*) FROM `analytics.events`
WHERE event_date BETWEEN '2026-08-01' AND '2026-08-07'
AND merchant_id = 'MERCH_00042';
-- Does NOT prune: partition column wrapped in a function
SELECT COUNT(*) FROM `analytics.events`
WHERE FORMAT_DATE('%Y-%m', event_date) = '2026-08';
-- Retrofit the guard rail on an existing table
ALTER TABLE `analytics.events`
SET OPTIONS (require_partition_filter = TRUE);
Key Points
- Partitioning skips segments, clustering skips blocks inside a partition
- Pruning needs a constant predicate directly on the partition column
- Clustering allows up to four columns and order determines effectiveness
- Dry run does not reflect clustering savings, only partition pruning
- require_partition_filter rejects unbounded scans before they run
Q25How do BigQuery reservations and slots work, and how do you find which queries are consuming them?
IntermediateBigQuery
Answer
A slot is a unit of compute: a virtual CPU with associated memory that executes one unit of a query's execution plan. In on-demand mode Google allocates slots dynamically from a shared pool with a project-level ceiling, and you never see them on the bill. With editions you buy capacity through a commitment or an autoscaling reservation, then create assignments that map projects, folders or the organisation to a reservation for a job type (QUERY, PIPELINE for load and export jobs, ML_EXTERNAL, BACKGROUND for background operations such as automatic reclustering).
A reservation has a baseline of always-available slots and an autoscale maximum; autoscale slots are billed per slot-hour only while in use, in increments. Idle slot sharing lets one reservation borrow unused slots from another in the same admin project unless you disable it, which is how a small ETL reservation can absorb a burst without you overprovisioning. The interview question underneath all this is usually about contention: when a query is slow but the SQL has not changed, you look at slot availability, not at the query.
INFORMATION_SCHEMA.JOBS_TIMELINE_BY_PROJECT gives per-second slot usage and lets you see whether a job was starved, while JOBS_BY_PROJECT exposes total_slot_ms, total_bytes_billed and the query plan stages. A well-run analytics platform separates reservations so ad-hoc analyst queries cannot starve the hourly pipeline, sets query queueing so excess concurrency waits rather than fails, and monitors the ratio of total_slot_ms to elapsed time to know whether more slots would actually help or whether the query is skewed and would not parallelise anyway.
-- Slot consumption by job over the last day
SELECT job_id, user_email,
ROUND(total_slot_ms / 1000 / 60, 1) AS slot_minutes,
TIMESTAMP_DIFF(end_time, start_time, SECOND) AS elapsed_s,
ROUND(SAFE_DIVIDE(total_slot_ms, TIMESTAMP_DIFF(end_time, start_time, MILLISECOND)), 1)
AS avg_slots_used,
ROUND(total_bytes_billed / POW(1024, 3), 1) AS gib_billed
FROM `region-asia-south1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT
WHERE creation_time > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 24 HOUR)
AND job_type = 'QUERY' AND state = 'DONE' AND error_result IS NULL
ORDER BY slot_minutes DESC
LIMIT 25;
-- Second-by-second view: was the job starved or just skewed?
SELECT period_start, job_id, period_slot_ms / 1000 AS slot_seconds
FROM `region-asia-south1`.INFORMATION_SCHEMA.JOBS_TIMELINE_BY_PROJECT
WHERE job_id = 'bquxjob_1a2b3c4d_1234567890ab'
ORDER BY period_start;
-- Autoscaling reservation, then assign a project to it
-- bq mk --reservation --location=asia-south1 --edition=ENTERPRISE \
-- --slots=100 --autoscale_max_slots=500 etl
-- bq mk --reservation_assignment --reservation_id=etl \
-- --job_type=QUERY --assignee_type=PROJECT --assignee_id=my-etl-project
Key Points
- Slots are compute units; editions buy them as baseline plus autoscale
- Assignments map projects to reservations per job type
- Idle slot sharing lets reservations borrow unused capacity
- JOBS_BY_PROJECT gives total_slot_ms, JOBS_TIMELINE shows starvation
- Separate reservations isolate ad-hoc analysts from production pipelines
Q26Explain Pub/Sub exactly-once delivery, ordering keys and dead letter topics. What are the caveats?
IntermediatePub/Sub
Answer
Pub/Sub is at-least-once by default, so every consumer must be idempotent. Exactly-once delivery is an opt-in property of a subscription and it means Pub/Sub will not redeliver a message once it has been successfully acknowledged, and will not deliver the same message concurrently to two subscribers within the ack deadline. The caveat interviewers want: it is exactly-once delivery within Pub/Sub, not end-to-end exactly-once processing.
If your handler writes to a database and then crashes before acking, the message is redelivered and your write happens twice, so you still need an idempotency key. Exactly-once also requires regional endpoints, reduces maximum throughput compared to the default mode, and changes the ack API so acks can fail and must be checked; client libraries expose an ack response you should actually inspect. Ordering keys guarantee that messages published with the same key to the same region are delivered in publish order to a subscriber, which is what you want for per-entity event streams such as one order or one user.
The cost is throughput per key and head-of-line blocking: if a message with key K cannot be processed, everything behind K stalls, so a poison message with ordering enabled halts that key indefinitely. Dead letter topics solve that by forwarding a message to another topic after a configured number of delivery attempts (5 to 100). Two operational requirements are easy to forget: the Pub/Sub service account needs roles/pubsub.publisher on the dead letter topic and roles/pubsub.subscriber on the source subscription, and a dead letter topic needs its own subscription or the messages simply expire.
# Ordering plus a dead letter topic after 5 failed attempts
gcloud pubsub topics create order-events-dlq
gcloud pubsub subscriptions create order-events-dlq-sub --topic=order-events-dlq
gcloud pubsub subscriptions create order-events-worker \
--topic=order-events \
--enable-message-ordering \
--enable-exactly-once-delivery \
--ack-deadline=60 \
--dead-letter-topic=order-events-dlq \
--max-delivery-attempts=5
# The Pub/Sub service agent needs these two grants or DLQ silently does nothing
PROJECT_NUMBER=123456789012
SA="service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com"
gcloud pubsub topics add-iam-policy-binding order-events-dlq \
--member="serviceAccount:${SA}" --role='roles/pubsub.publisher'
gcloud pubsub subscriptions add-iam-policy-binding order-events-worker \
--member="serviceAccount:${SA}" --role='roles/pubsub.subscriber'
// Publish with an ordering key (client must set the regional endpoint)
// await pubsub.topic('order-events', { messageOrdering: true })
// .publishMessage({ json: evt, orderingKey: evt.orderId });
Key Points
- Default is at-least-once; handlers must be idempotent regardless
- Exactly-once covers delivery, not your downstream side effects
- Ordering keys serialise per key and create head-of-line blocking
- Dead letter topics need the Pub/Sub service agent granted publisher rights
- A dead letter topic without a subscription just discards messages
Q27Compare Private Google Access, Private Service Connect, VPC peering and Shared VPC.
IntermediateNetworking
Answer
These four solve different problems and mixing them up is a common interview stumble. Private Google Access is a subnet-level setting that lets VMs without external IP addresses reach Google APIs and services over the default internet gateway route using Google's public IP ranges, but without traffic leaving Google's network. It is the minimum you enable on every private subnet, otherwise a VM with no external IP cannot even reach storage.googleapis.com.
Private Service Connect goes further: it creates an endpoint with an internal IP inside your VPC that maps to a Google API bundle or to a service published by another VPC, including third-party SaaS. That means API traffic uses an RFC 1918 address you control, DNS resolves inside your network, and you can apply VPC Service Controls cleanly. PSC is also the modern way to consume managed services such as Cloud SQL or a partner's service without the address-space and transitivity problems of peering.
VPC Network Peering connects two VPCs so they route privately using internal IPs, but it is non-transitive (A peered to B and B peered to C does not give A to C), CIDR ranges must not overlap, and there are quota limits on peered routes; it also cannot be undone selectively once address space collides. Shared VPC is an organisational construct rather than a connectivity one: a host project owns the network and subnets, and service projects attach so their resources use those subnets while IAM stays separate. Shared VPC is what most enterprises in India use to give application teams their own projects while the network team retains control of routing, firewalls and Interconnect.
# Private Google Access: mandatory on subnets whose VMs have no external IP
gcloud compute networks subnets update prod-mum \
--region=asia-south1 --enable-private-ip-google-access
# Private Service Connect endpoint for all Google APIs on an internal IP
gcloud compute addresses create psc-apis --global \
--purpose=PRIVATE_SERVICE_CONNECT --addresses=10.99.0.2 --network=prod-vpc
gcloud compute forwarding-rules create psc-google-apis --global \
--network=prod-vpc --address=psc-apis --target-google-apis-bundle=all-apis
# Shared VPC: host project owns the network, service projects attach
gcloud compute shared-vpc enable net-host-prj
gcloud compute shared-vpc associated-projects add app-team-prj --host-project=net-host-prj
gcloud compute networks subnets add-iam-policy-binding prod-mum \
--region=asia-south1 --project=net-host-prj \
--member='serviceAccount:123456789012@cloudservices.gserviceaccount.com' \
--role='roles/compute.networkUser'
# Peering is non-transitive and needs non-overlapping CIDRs
gcloud compute networks peerings create prod-to-data \
--network=prod-vpc --peer-project=data-prj --peer-network=data-vpc \
--import-custom-routes --export-custom-routes
Key Points
- Private Google Access is a subnet flag for reaching Google APIs without external IPs
- PSC gives Google APIs and partner services an internal IP inside your VPC
- VPC peering is non-transitive and forbids overlapping CIDRs
- Shared VPC separates network ownership from application project IAM
- PSC is the modern path for private managed-service consumption
Q28What problem do VPC Service Controls solve that IAM and firewall rules cannot?
IntermediateSecurity
Answer
IAM answers 'who can call this API'. Firewall rules answer 'which network traffic reaches this VM'. Neither answers 'can an authorised identity move data out of my perimeter', and that is the exfiltration risk VPC Service Controls addresses.
A service perimeter wraps a set of projects and a set of restricted services (BigQuery, Cloud Storage, Pub/Sub, Cloud SQL admin, and dozens more). Once a project is inside, API calls to those services from outside the perimeter are denied even when the caller holds a valid IAM role and a valid token. That means a leaked service account key used from an attacker's laptop cannot read your BigQuery dataset, and an insider with legitimate roles/bigquery.dataViewer cannot copy a table into their personal project, because the destination is outside the perimeter.
You permit deliberate crossings with ingress and egress rules that specify identity, source, target service and method, and with access levels from Access Context Manager based on IP range, device policy or user identity. Perimeter bridges connect two perimeters for specific shared projects. The operational reality is that VPC-SC breaks things in non-obvious ways, so you always start in dry-run mode, which logs what would have been denied without denying it, and read those violation logs for weeks before enforcing.
Typical breakages include Cloud Build pulling from a public source, a Looker Studio dashboard querying BigQuery from outside, gcloud from a home internet connection, and any service-to-service call that transits a Google-managed endpoint you did not add to the perimeter. Indian financial services and healthcare customers use it heavily because it is the cleanest technical answer to a regulator asking how data cannot leave a controlled boundary.
# Access level: only from the office egress ranges
gcloud access-context-manager levels create office_only \
--policy=POLICY_ID --title='Office network only' \
--basic-level-spec=- <<'EOF'
- ipSubnetworks:
- 203.0.113.0/24
- 198.51.100.0/24
EOF
# Perimeter in dry-run first, always
gcloud access-context-manager perimeters create prod_data \
--policy=POLICY_ID --title='Production data' \
--resources=projects/123456789012 \
--restricted-services=bigquery.googleapis.com,storage.googleapis.com \
--perimeter-type=regular --dry-run
# Read what would have been blocked before enforcing
gcloud logging read \
'protoPayload.metadata.dryRun="true" AND severity>=ERROR AND
protoPayload.status.details.violations.type="SERVICE_PERIMETER"' \
--limit=50 --format='value(protoPayload.authenticationInfo.principalEmail,protoPayload.serviceName)'
# Promote dry-run config to enforced
gcloud access-context-manager perimeters dry-run enforce prod_data --policy=POLICY_ID
Key Points
- Stops data exfiltration by authorised identities, which IAM cannot
- Restricted services are denied across the perimeter boundary regardless of IAM
- Ingress and egress rules plus access levels allow deliberate crossings
- Always run dry-run mode and read violation logs before enforcing
- Common breakages: CI pulls, BI tools, gcloud from outside the allowed ranges
Q29How do you make Cloud SQL highly available, and what is the right way for a Cloud Run service to connect to it?
IntermediateCloud SQL
Answer
Cloud SQL HA is a regional configuration: the instance gets a standby in a second zone with synchronous replication of the write-ahead log, and on primary failure Cloud SQL fails over by moving the instance's IP to the standby. Failover typically takes tens of seconds, during which connections drop, so your application needs retry logic and a connection pool that recovers. HA roughly doubles cost and does not protect against a regional outage or a bad migration, that is what cross-region read replicas and point-in-time recovery are for.
Read replicas are asynchronous, can live in other regions, can be promoted to standalone primaries (which breaks replication permanently), and are the tool for read scaling and for cross-region DR. Enable automated backups and binary logging or WAL archiving, because point-in-time recovery is what saves you from an accidental DELETE without a WHERE clause. For connectivity, the wrong answer is a public IP with authorised networks and a password. The right answer for Cloud Run is either the built-in Cloud SQL connection using the instance connection name, which mounts a Unix socket at /cloudsql/PROJECT:REGION:INSTANCE, or Direct VPC egress to the instance's private IP.
Both should be combined with IAM database authentication so the application authenticates as its service account and there is no password to leak. Also cap connections deliberately: Cloud SQL enforces a max_connections limit that scales with machine size, and a serverless front end multiplies concurrency by instance count, so either keep pools tiny or put PgBouncer in front. In Indian fintech reviews, interviewers routinely ask about the failover window and whether your retry policy distinguishes a transient failover error from a genuine constraint violation.
# Regional HA, private IP only, PITR enabled
gcloud sql instances create orders-db \
--database-version=POSTGRES_16 --region=asia-south1 \
--availability-type=REGIONAL --tier=db-custom-4-16384 \
--network=projects/net-host-prj/global/networks/prod-vpc --no-assign-ip \
--enable-point-in-time-recovery --backup-start-time=19:00 \
--database-flags=cloudsql.iam_authentication=on,max_connections=400
# Cross-region read replica for DR
gcloud sql instances create orders-db-dr \
--master-instance-name=orders-db --region=asia-south2
# IAM database user, no password anywhere
gcloud sql users create orders-api@my-prod-api.iam \
--instance=orders-db --type=CLOUD_IAM_SERVICE_ACCOUNT
# Cloud Run attaches the instance and exposes a Unix socket
gcloud run deploy orders-api --region=asia-south1 \
--add-cloudsql-instances=my-prod-api:asia-south1:orders-db \
--set-env-vars=PGHOST=/cloudsql/my-prod-api:asia-south1:orders-db,PGUSER=orders-api@my-prod-api.iam
Key Points
- REGIONAL availability adds a synchronous standby in a second zone
- Failover takes tens of seconds and drops existing connections
- Read replicas are asynchronous; promotion is one-way
- Use the Cloud SQL connector or Direct VPC egress, not public IP
- IAM database authentication removes stored passwords entirely
Q30How would you structure Terraform for a multi-environment GCP setup, and what state and identity settings matter?
IntermediateInfrastructure as Code
Answer
The layout that survives contact with a real team is one root module per environment plus shared child modules, with state in a dedicated Cloud Storage bucket that has object versioning enabled and lives in a separate project from the workloads it manages. Terraform's GCS backend supports state locking natively, so you do not need a DynamoDB-equivalent as you would on AWS. Use a bucket prefix per root module so environments never share a state file, and never commit tfvars with secrets, read them from Secret Manager or pass them from CI.
On identity, run Terraform through service account impersonation rather than a key: the provider takes impersonate_service_account, and CI authenticates with Workload Identity Federation, which means the pipeline holds no credential at rest. Grant the Terraform service account roles at the folder level so it can create projects and attach billing, and keep a separate, less privileged identity for plan-only runs on pull requests. Practical details interviewers look for: google versus google-beta providers, where beta resources need the explicit provider alias; the difference between the google_project_iam_member resource (additive, safe) and google_project_iam_policy (authoritative, will happily delete every binding it does not know about, including Google-managed service agents); lifecycle blocks with prevent_destroy on databases and buckets; and the fact that many GCP APIs must be enabled with google_project_service before the resources using them can be created, which is a classic first-apply failure. Finally, pin provider and module versions, because GCP provider majors do introduce breaking schema changes and an unpinned upgrade mid-sprint will produce a plan nobody wants to read.
terraform {
required_version = ">= 1.9"
required_providers {
google = { source = "hashicorp/google", version = "~> 6.0" }
}
backend "gcs" {
bucket = "acme-tfstate-prod"
prefix = "env/prod/network"
}
}
provider "google" {
project = var.project_id
region = "asia-south1"
impersonate_service_account = "terraform@acme-tf-admin.iam.gserviceaccount.com"
}
resource "google_project_service" "run" {
service = "run.googleapis.com"
disable_on_destroy = false
}
# Additive, does not clobber other bindings
resource "google_project_iam_member" "api_secrets" {
project = var.project_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.api.email}"
}
resource "google_sql_database_instance" "orders" {
name = "orders-db"
database_version = "POSTGRES_16"
region = "asia-south1"
settings { tier = "db-custom-4-16384", availability_type = "REGIONAL" }
deletion_protection = true
lifecycle { prevent_destroy = true }
}
Key Points
- GCS backend gives native state locking, one prefix per root module
- Impersonation plus Workload Identity Federation means no keys in CI
- google_project_iam_member is additive; _iam_policy is destructive
- Enable APIs with google_project_service before dependent resources
- Pin provider versions, GCP provider majors change resource schemas
Q31How does Secret Manager work, and how do you consume a secret from Cloud Run or GKE safely?
IntermediateSecrets
Answer
Secret Manager stores immutable versions under a named secret. You never update a version, you add a new one, and each version has a state (ENABLED, DISABLED, DESTROYED). Consumers reference either a pinned version number or the alias 'latest'.
That choice is the interesting trade-off: 'latest' means a rotation takes effect without a redeploy, but it also means a bad secret version breaks production instantly with no rollback path other than adding another version, and it makes your deployments non-reproducible. Pinned versions are auditable and safe but require a deploy to rotate. Access is IAM-controlled per secret with roles/secretmanager.secretAccessor, which should be granted on the individual secret rather than the project, and every access is recorded in Cloud Audit Logs, so you can answer 'who read the payment gateway key last month'.
Replication is either automatic or user-managed, and user-managed replication pinned to asia-south1 is what you use when a data residency policy says key material must not leave India. Rotation schedules publish a message to a Pub/Sub topic on a cadence, but Secret Manager does not rotate credentials for you, you write the handler that talks to the upstream system. For Cloud Run you either mount a secret as an environment variable or as a file in a volume, and the file form is preferable for anything multi-line such as a private key, plus it avoids the secret appearing in environment dumps and crash reports. On GKE, the Secret Manager CSI driver mounts secrets as files backed by Workload Identity, which is better than syncing them into etcd as Kubernetes Secrets where they are only base64-encoded by default.
# Create with residency-pinned replication, then add a version
gcloud secrets create razorpay-key \
--replication-policy=user-managed --locations=asia-south1
printf '%s' "$KEY_MATERIAL" | gcloud secrets versions add razorpay-key --data-file=-
# Grant on the individual secret, never at project level
gcloud secrets add-iam-policy-binding razorpay-key \
--member='serviceAccount:orders-api@my-prod-api.iam.gserviceaccount.com' \
--role='roles/secretmanager.secretAccessor'
# Cloud Run: env var pinned to a version, plus a file mount for a private key
gcloud run deploy orders-api --region=asia-south1 \
--set-secrets='RAZORPAY_KEY=razorpay-key:7' \
--set-secrets='/secrets/jwt/private.pem=jwt-signing-key:latest'
# Rotation reminder into Pub/Sub (you still write the rotation logic)
gcloud secrets update razorpay-key \
--next-rotation-time='2026-11-01T00:00:00Z' --rotation-period='7776000s' \
--topic=projects/my-prod-api/topics/secret-rotation
# Audit: who accessed it
gcloud logging read 'protoPayload.methodName="google.cloud.secretmanager.v1.SecretManagerService.AccessSecretVersion"' --limit=20
Key Points
- Versions are immutable; rotation means adding a new version
- 'latest' rotates without redeploy but removes reproducibility
- Grant secretAccessor on the secret, not the whole project
- User-managed replication pins secrets to asia-south1 for residency
- Mount as files for multi-line material and to keep it out of env dumps
Q32What causes hotspotting in Cloud Spanner, and how do interleaved tables and Data Boost help?
IntermediateSpanner
Answer
Spanner shards data into splits by primary key range and distributes splits across servers. Because splits are contiguous key ranges, a monotonically increasing primary key such as an auto-increment integer or a plain timestamp sends every new write to the same split, and that single split becomes the bottleneck no matter how many nodes you buy. This is the number one Spanner design question in interviews.
The fixes are all about spreading key space: use a UUIDv4 or a hash-prefixed key, bit-reverse a sequence (Spanner offers bit-reversed sequences precisely for this), or prefix the key with a shard id computed from a hash of the logical id. If you genuinely need time-ordered reads, keep the timestamp as a secondary component of the key rather than the leading column. Interleaved tables physically co-locate child rows inside the parent row's split, so fetching an order and all its line items is a single split read rather than a distributed join, and cascading deletes come free.
The constraint is that the child's primary key must be prefixed by the parent's, and interleaving is a schema decision you cannot change later without a migration. Secondary indexes have the same hotspot rules as tables, and STORING clauses let an index answer a query without a base-table lookup at the cost of write amplification. Data Boost provides independent serverless compute for analytical reads so a heavy export or federated BigQuery query does not consume the compute serving your transactional traffic. And remember Spanner scales by processing units, with 1000 units making one node, so small workloads can start well below a full node.
-- Hotspot: every insert lands at the end of the key space
CREATE TABLE BadOrders (
OrderId INT64 NOT NULL, -- monotonically increasing
CreatedAt TIMESTAMP NOT NULL,
) PRIMARY KEY (OrderId);
-- Spread writes with a UUID, and interleave the children
CREATE TABLE Orders (
OrderId STRING(36) NOT NULL, -- UUIDv4
MerchantId STRING(32) NOT NULL,
CreatedAt TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp = true),
AmountPaise INT64 NOT NULL,
) PRIMARY KEY (OrderId);
CREATE TABLE OrderItems (
OrderId STRING(36) NOT NULL,
ItemId INT64 NOT NULL,
Sku STRING(64) NOT NULL,
Qty INT64 NOT NULL,
) PRIMARY KEY (OrderId, ItemId),
INTERLEAVE IN PARENT Orders ON DELETE CASCADE;
-- Index that answers the query without touching the base table
CREATE INDEX OrdersByMerchant
ON Orders (MerchantId, CreatedAt DESC)
STORING (AmountPaise);
-- Bit-reversed sequence when you must have an integer key
CREATE SEQUENCE OrderSeq OPTIONS (sequence_kind = 'bit_reversed_positive');
Key Points
- Monotonic primary keys concentrate writes on one split
- UUIDs, hash prefixes or bit-reversed sequences spread the key space
- Interleaving co-locates child rows and makes parent-child reads local
- Secondary indexes hotspot by the same rules; STORING avoids base lookups
- Data Boost isolates analytical reads from transactional compute
Q33When do you reach for Dataflow versus Dataproc, and what do windowing and watermarks mean in a streaming pipeline?
IntermediateData Processing
Answer
Dataproc is managed Hadoop and Spark: you get a cluster, you run Spark, Hive, Presto or Flink jobs on it, and you keep your existing Spark code and skills. It is the migration answer for teams moving an on-premise Hadoop estate, and with ephemeral clusters plus Spot secondary workers it can be very cheap. Dataproc Serverless removes cluster management for Spark batch specifically.
Dataflow is the fully managed Apache Beam runner: you write one pipeline in the Beam SDK (Java, Python or Go) and run it in batch or streaming mode with autoscaling, dynamic work rebalancing to fix stragglers, and Streaming Engine which moves shuffle state off the worker VMs. Choose Dataflow for new streaming work and for pipelines where you do not want to think about cluster sizing; choose Dataproc when you have existing Spark assets or need the Hadoop ecosystem. On semantics, Beam distinguishes event time (when the thing happened) from processing time (when your pipeline saw it), and windows group elements by event time: fixed windows of a constant duration, sliding windows that overlap, and session windows that close after a gap of inactivity, which is the natural fit for user activity streams.
A watermark is the runner's estimate of how far event time has advanced, and it decides when a window can fire. Data arriving after the watermark passes is late data, handled by allowed lateness plus a trigger and an accumulation mode that decides whether late results replace or add to earlier output. Getting these three concepts right, windows, watermarks and triggers, is what a data engineering interviewer is actually testing when they ask about Dataflow.
# Apache Beam (Python) streaming pipeline on Dataflow
# import apache_beam as beam
# from apache_beam import window
# from apache_beam.options.pipeline_options import PipelineOptions
#
# opts = PipelineOptions(streaming=True, region='asia-south1',
# max_num_workers=20, enable_streaming_engine=True)
#
# with beam.Pipeline(options=opts) as p:
# (p
# | 'Read' >> beam.io.ReadFromPubSub(subscription=SUB, with_attributes=False)
# | 'Parse' >> beam.Map(parse_event)
# | 'Timestamp' >> beam.Map(
# lambda e: beam.window.TimestampedValue(e, e['event_ts']))
# | 'Window' >> beam.WindowInto(
# window.FixedWindows(60),
# trigger=beam.transforms.trigger.AfterWatermark(
# late=beam.transforms.trigger.AfterCount(1)),
# allowed_lateness=300,
# accumulation_mode=beam.transforms.trigger.AccumulationMode.ACCUMULATING)
# | 'CountByMerchant' >> beam.CombinePerKey(sum)
# | 'ToBQ' >> beam.io.WriteToBigQuery(
# 'my-prod-api:analytics.per_minute_gmv',
# write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND))
# Ephemeral Dataproc cluster with Spot secondary workers
gcloud dataproc clusters create etl-tmp --region=asia-south1 \
--num-workers=2 --num-secondary-workers=8 --secondary-worker-type=spot \
--max-idle=30m --enable-component-gateway
Key Points
- Dataproc for existing Spark and Hadoop assets, Dataflow for new streaming
- Beam separates event time from processing time
- Fixed, sliding and session windows group by event time
- Watermarks decide when a window fires; allowed lateness handles stragglers
- Streaming Engine moves shuffle state off workers and improves autoscaling
Q34How do you add Memorystore caching to a Cloud Run service, and what changed with Direct VPC egress?
IntermediateCaching and Networking
Answer
Memorystore provides managed Redis, Valkey and Memcached with private IPs inside your VPC, which means a serverless service cannot reach it over the public internet by design. Historically the only bridge was a Serverless VPC Access connector: a managed pool of e2-micro-class instances that proxied traffic from the serverless environment into your VPC, with throughput tied to the connector's instance count and a real cold-scaling cost. Direct VPC egress replaced that for Cloud Run: the revision gets network interfaces directly in your subnet, removing the connector hop, reducing latency, scaling with the service rather than with a separate resource, and costing nothing extra beyond the IP addresses consumed.
The trade-off is IP consumption, each instance takes an address from the subnet, so a service that scales to 200 instances needs a subnet sized for it, and running out produces scaling failures that look mysterious if you have not planned the CIDR. On the caching side, the patterns interviewers expect are cache-aside for read-heavy endpoints, a short TTL plus jitter to avoid a synchronised stampede when a popular key expires, and single-flight or a lock key so that on a miss only one instance recomputes while others wait. Also remember the serverless multiplier problem again: Redis has a connection limit and Cloud Run can create hundreds of instances, so use a small pool per instance and enable lazy connections. For high availability choose the Standard tier with a replica, and know that a failover invalidates in-flight connections, so your client must reconnect rather than treating a Redis error as fatal.
# Memorystore for Redis with a replica, private IP in the prod VPC
gcloud redis instances create sessions --region=asia-south1 \
--tier=standard --size=5 --redis-version=redis_7_x \
--network=projects/net-host-prj/global/networks/prod-vpc \
--connect-mode=PRIVATE_SERVICE_ACCESS --transit-encryption-mode=SERVER_AUTHENTICATION
# Direct VPC egress instead of a Serverless VPC Access connector
gcloud run deploy orders-api --region=asia-south1 \
--network=prod-vpc --subnet=prod-mum \
--vpc-egress=private-ranges-only \
--set-env-vars=REDIS_HOST=10.20.3.5,REDIS_PORT=6378
// Cache-aside with jittered TTL and a small per-instance pool
// const key = `merchant:${id}:summary`;
// const hit = await redis.get(key);
// if (hit) return JSON.parse(hit);
// const fresh = await db.loadMerchantSummary(id);
// const ttl = 300 + Math.floor(Math.random() * 60); // jitter kills stampedes
// await redis.set(key, JSON.stringify(fresh), { EX: ttl });
// return fresh;
Key Points
- Memorystore is private-IP only, serverless needs a VPC path to reach it
- Direct VPC egress removes the Serverless VPC Access connector hop
- Each Cloud Run instance consumes a subnet IP under Direct VPC egress
- Jittered TTLs and single-flight prevent cache stampedes
- Standard tier gives a replica; clients must handle failover reconnects
Q35Your GCP bill jumped 40% month over month. Walk through how you would find and fix it.
IntermediateCost Optimisation
Answer
Start with data, not intuition. Query the detailed billing export in BigQuery grouped by service, SKU, project and day, and diff the current month against the previous one at SKU level, because the service level is too coarse: 'Compute Engine' going up tells you nothing, while 'Network Internet Egress from Mumbai to Americas' or 'BigQuery Analysis' tells you everything. Look at the day the curve changed and correlate with deploys and audit logs.
The usual suspects, in the order they actually occur: a BigQuery query pattern change or a new dashboard scanning unpartitioned tables; log ingestion into _Default from a debug-level logger left on after an incident; inter-region or internet egress from a misconfigured backend, for example a GKE cluster in asia-south1 pulling images from a us multi-region Artifact Registry on every pod start; a Cloud Run service given min-instances or --no-cpu-throttling and now billed continuously; orphaned resources such as unattached persistent disks, idle load balancer forwarding rules, reserved static IPs not in use, and snapshots with no retention policy; and NAT gateway data processing charges from a chatty service. Then apply the structural fixes: committed use discounts for the steady baseline of Compute and for Cloud Run and GKE Autopilot spend, Spot for batch, autoscaling floors reviewed, BigQuery reservations if on-demand spend has become predictable, storage lifecycle rules, and log exclusions. Use the Recommender API to sweep idle resources automatically. Finally close the loop: budgets with Pub/Sub alerts per team label, a weekly scheduled query that posts the top ten SKU deltas, and a policy that any new project inherits label enforcement so next month's investigation takes ten minutes instead of a day.
-- SKU-level month-over-month diff from the detailed billing export
WITH daily AS (
SELECT DATE(usage_start_time) AS d,
project.id AS project_id,
service.description AS service,
sku.description AS sku,
SUM(cost) AS cost
FROM `my-billing.billing_export.gcp_billing_export_resource_v1_01ABCD_2345EF_67890A`
WHERE DATE(usage_start_time) >= DATE_SUB(CURRENT_DATE(), INTERVAL 60 DAY)
GROUP BY d, project_id, service, sku
)
SELECT project_id, service, sku,
ROUND(SUM(IF(d >= DATE_TRUNC(CURRENT_DATE(), MONTH), cost, 0)), 2) AS this_month,
ROUND(SUM(IF(d < DATE_TRUNC(CURRENT_DATE(), MONTH), cost, 0)), 2) AS last_month,
ROUND(SUM(IF(d >= DATE_TRUNC(CURRENT_DATE(), MONTH), cost, -cost)), 2) AS delta
FROM daily
GROUP BY project_id, service, sku
HAVING delta > 5000
ORDER BY delta DESC
LIMIT 30;
# Sweep the obvious waste
gcloud compute disks list --filter='-users:*' --format='table(name,zone,sizeGb)'
gcloud compute addresses list --filter='status=RESERVED' --format='table(name,region)'
gcloud recommender recommendations list --project=my-prod-api \
--location=asia-south1-a --recommender=google.compute.instance.MachineTypeRecommender
Key Points
- Diff at SKU level, not service level, and correlate with the deploy timeline
- Top causes: BigQuery scans, log ingestion, egress, min-instances, orphans
- Committed use discounts cover the steady baseline, Spot covers batch
- Recommender finds idle disks, unused IPs and oversized machines
- Close the loop with per-team budgets and a scheduled SKU-delta report
Q36How do Cloud Armor and Cloud CDN protect and accelerate an application behind a global load balancer?
IntermediateEdge Security
Answer
Cloud Armor is the WAF and DDoS layer attached to a backend service on an external Application Load Balancer, evaluated at Google's edge before traffic reaches your origin. A security policy is an ordered list of rules by priority, each with a match expression written in CEL-like syntax over origin.region_code, request headers, paths and IP ranges, and an action of allow, deny with a status code, throttle, rate_based_ban, or redirect to reCAPTCHA. Preconfigured WAF rules implement the OWASP ModSecurity Core Rule Set for SQL injection, cross-site scripting, local and remote file inclusion, with a sensitivity level from 0 to 4 that trades detection against false positives.
Rate-based rules are the practical workhorse: count requests per client IP or per header over an interval and ban offenders for a period, which stops credential stuffing and scraper traffic long before it touches your Cloud Run instances. Adaptive Protection uses machine learning to detect Layer 7 attacks and propose rules. Always deploy new rules in preview mode first and read the enforced_security_policy logs, because a badly tuned SQLi rule will block legitimate traffic containing an apostrophe.
Cloud CDN is enabled on the same backend service and caches responses at Google's edge points of presence, including in India, using cache modes (CACHE_ALL_STATIC, USE_ORIGIN_HEADERS or FORCE_CACHE_ALL), configurable cache keys that let you strip irrelevant query parameters or vary on a header, negative caching for 404s, and signed URLs or signed cookies for private content. Cache invalidation is by path pattern and takes a short time to propagate globally, so for versioned assets prefer content-hashed filenames over relying on invalidation.
# Rate-based ban: 100 requests per minute per IP on the login path
gcloud compute security-policies create edge-policy --description='Prod WAF'
gcloud compute security-policies rules create 1000 --security-policy=edge-policy \
--expression="request.path.matches('/api/auth/login')" \
--action=rate-based-ban --rate-limit-threshold-count=100 \
--rate-limit-threshold-interval-sec=60 --ban-duration-sec=600 \
--conform-action=allow --exceed-action=deny-429 --enforce-on-key=IP
# OWASP SQLi in preview mode until the logs look clean
gcloud compute security-policies rules create 2000 --security-policy=edge-policy \
--expression="evaluatePreconfiguredExpr('sqli-v33-stable', ['owasp-crs-v030301-id942251-sqli'])" \
--action=deny-403 --preview
gcloud compute backend-services update orders-backend --global \
--security-policy=edge-policy
# CDN with a cache key that ignores tracking parameters
gcloud compute backend-services update orders-backend --global \
--enable-cdn --cache-mode=CACHE_ALL_STATIC \
--default-ttl=3600 --max-ttl=86400 --negative-caching \
--cache-key-query-string-blacklist=utm_source,utm_medium,gclid
Key Points
- Cloud Armor rules are priority-ordered CEL expressions at the edge
- Preconfigured OWASP CRS rules have a tunable sensitivity level
- Rate-based bans stop credential stuffing before it reaches the origin
- Always use --preview and read enforced_security_policy logs first
- Strip tracking query parameters from the CDN cache key to raise hit rate
Q37How do Organization Policy constraints, IAM deny policies and tags combine to enforce guardrails at scale?
AdvancedGovernance
Answer
Three mechanisms operate at different layers and a strong answer explains why you need all three. Organization Policy constraints restrict resource configuration regardless of IAM. Boolean constraints such as constraints/compute.requireShieldedVm, constraints/sql.restrictPublicIp, constraints/storage.publicAccessPrevention and constraints/iam.disableServiceAccountKeyCreation either enforce or do not.
List constraints such as constraints/gcp.resourceLocations restrict allowed values, and setting it to in:asia-south1-locations and in:asia-south2-locations is the technical control behind an India data residency commitment. Custom Organization Policies extend this with CEL expressions over resource fields, letting you write rules Google never shipped, for example rejecting any Cloud Run service without a specific label. Policies inherit down the hierarchy and a child can override with a merge or a replacement unless you mark the parent policy as not overridable.
IAM deny policies operate on permissions rather than configuration: they attach to an organisation, folder or project and deny a permission set to specified principals even if an allow policy grants it, with optional exception principals. Deny is evaluated before allow, which finally gives you the subtractive control that plain IAM lacks, so you can let a team hold roles/editor for velocity while denying iam.serviceAccountKeys.create and billing changes outright. Resource Manager tags are the glue: they inherit through the hierarchy, can be attached to projects and many resources, and are usable in both IAM conditions and Organization Policy conditions, so you can express 'this constraint applies only to resources tagged environment=production'. Roll all of this out with dry-run and audit-log review, because a badly scoped constraint can break every deployment pipeline in the organisation at once.
# Pin all resource creation to Indian regions
cat > locations.yaml <<'EOF'
name: organizations/123456789012/policies/gcp.resourceLocations
spec:
rules:
- values:
allowedValues:
- in:asia-south1-locations
- in:asia-south2-locations
EOF
gcloud org-policies set-policy locations.yaml
# Hard-block public buckets and public Cloud SQL IPs
gcloud resource-manager org-policies enable-enforce \
constraints/storage.publicAccessPrevention --organization=123456789012
gcloud resource-manager org-policies enable-enforce \
constraints/sql.restrictPublicIp --organization=123456789012
# IAM deny: nobody creates SA keys, except the break-glass group
cat > deny-keys.json <<'EOF'
{
"displayName": "No service account keys",
"rules": [{
"denyRule": {
"deniedPrincipals": ["principalSet://goog/public:all"],
"exceptionPrincipals": ["principalSet://goog/group/breakglass@example.com"],
"deniedPermissions": ["iam.googleapis.com/serviceAccountKeys.create"]
}
}]
}
EOF
gcloud iam policies create no-sa-keys --attachment-point=organizations/123456789012 \
--kind=denypolicies --policy-file=deny-keys.json
Key Points
- Org Policy restricts configuration, IAM deny restricts permissions
- Deny is evaluated before allow and supports exception principals
- gcp.resourceLocations is the enforcement behind data residency claims
- Custom Org Policies use CEL over resource fields for bespoke rules
- Resource Manager tags scope both IAM conditions and Org Policy
Q38A Cloud Run service starts returning 503s and p99 latency triples under a traffic spike. How do you diagnose it?
AdvancedProduction Debugging
Answer
Cloud Run returns 503 for a small set of distinct reasons and each has a different signature, so the first job is to separate them. If the request never reached your container, the load balancer or the Cloud Run front end generated the error: check the request logs for the responseStatus and the specific message, and check the run.googleapis.com/container/instance_count and container/billable_instance_time metrics against your max-instances setting. Hitting max instances produces queueing then 429s and 503s, and the fix is raising the ceiling or raising concurrency.
If instances exist but requests time out, look at request latency broken down into startup latency versus request latency: a spike that correlates with new instance creation is cold starts, and the levers are min-instances, --cpu-boost, a smaller image, lazy initialisation and moving schema or config fetches out of the boot path. If the container is being killed, you will see 'Memory limit of X exceeded' in the logs and the instance count will churn; remember the Cloud Run filesystem is tmpfs, so anything written to disk counts against memory and a service that writes temp files under load will OOM in a way that looks random. If your handlers are fine but downstream calls hang, the real limit is usually the database: max-instances multiplied by pool size against the Cloud SQL connection cap, or a Redis instance refusing connections.
Check the upstream saturation before touching Cloud Run settings at all. Finally, if requests fail only during a deploy, you are missing SIGTERM handling or your startup probe passes before the app is genuinely ready. The rigorous approach is a Cloud Monitoring dashboard combining instance count, CPU utilisation, request count, request latency percentiles and the downstream connection count on one time axis, so the causal ordering is visible rather than guessed.
# 1. Are we hitting the instance ceiling?
gcloud monitoring time-series list \
--filter='metric.type="run.googleapis.com/container/instance_count" AND resource.labels.service_name="orders-api"' \
--interval-end-time="$(date -u +%Y-%m-%dT%H:%M:%SZ)" --interval-start-time="$(date -u -v-1H +%Y-%m-%dT%H:%M:%SZ)"
# 2. What did the failing requests actually say?
gcloud logging read '
resource.type="cloud_run_revision"
AND resource.labels.service_name="orders-api"
AND httpRequest.status>=500' \
--limit=50 --format='table(timestamp,httpRequest.status,httpRequest.latency,textPayload)'
# 3. OOM kills and container restarts
gcloud logging read '
resource.type="cloud_run_revision"
AND textPayload=~"Memory limit of .* exceeded"' --limit=20
# 4. Mitigations, applied one at a time and measured
gcloud run services update orders-api --region=asia-south1 \
--max-instances=200 --min-instances=5 --cpu-boost --memory=2Gi
Key Points
- Separate front-end 503s from container 503s using request logs first
- instance_count pinned at max-instances means a capacity ceiling, not a bug
- Cold starts show as startup latency, fixed with min-instances and cpu-boost
- tmpfs writes count against the memory limit and cause silent OOM kills
- Check the database connection ceiling before tuning Cloud Run at all
Q39Walk through the failure modes you have seen in a production GKE cluster and how you would prevent each.
AdvancedGKE Operations
Answer
Five recur constantly. First, OOMKilled pods: the container exceeds its memory limit and the kernel kills it, showing as exit code 137 and reason OOMKilled in the pod status. Prevent it by setting requests from observed usage plus headroom, setting limits deliberately, and for JVM or Node workloads making the runtime aware of the cgroup limit rather than the node's total memory.
Second, CPU throttling: because limits are enforced through CFS quota, a pod with a low CPU limit gets throttled in 100 millisecond periods, producing latency spikes that look like network problems; container_cpu_cfs_throttled_periods_total is the metric that proves it, and often the right fix is removing the CPU limit while keeping the request. Third, IP exhaustion: GKE allocates a pod CIDR block per node (a /24 by default for 110 pods), so the cluster's secondary range caps total nodes long before CPU does, and you see nodes stuck in NotReady or the autoscaler refusing to add capacity. Plan the secondary range up front or reduce max-pods-per-node.
Fourth, node upgrades stalling on PodDisruptionBudgets: a PDB with minAvailable equal to the replica count means no pod can ever be evicted, so a surge upgrade hangs indefinitely; PDBs must always leave one pod evictable. Fifth, autoscaler refusing to scale down because of pods with local storage, restrictive PDBs, or pods without a controller, which leaves you paying for idle nodes. Alongside these, run a maintenance window and release channel so control plane upgrades are predictable, use surge upgrades with the right maxSurge and maxUnavailable, and keep readiness probes honest because a probe that returns healthy during warm-up sends traffic to a pod that cannot serve it.
# The classic: exit 137, reason OOMKilled
kubectl -n orders get pod orders-api-7d9f -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}{"\n"}'
kubectl -n orders describe pod orders-api-7d9f | grep -A5 'Last State'
# Prove CPU throttling rather than guessing at network latency
kubectl -n orders exec deploy/orders-api -- \
cat /sys/fs/cgroup/cpu.stat
# Pod IP capacity: nodes are capped by the secondary range, not by CPU
gcloud container clusters describe prod-mum --region=asia-south1 \
--format='value(ipAllocationPolicy.clusterIpv4Cidr,defaultMaxPodsConstraint.maxPodsPerNode)'
# A PDB that never blocks an upgrade
# apiVersion: policy/v1
# kind: PodDisruptionBudget
# metadata: { name: orders-api, namespace: orders }
# spec:
# maxUnavailable: 1 # not minAvailable: <replicas>
# selector: { matchLabels: { app: orders-api } }
# Why did the autoscaler not remove that idle node?
kubectl get events -A --field-selector reason=ScaleDown
kubectl -n kube-system get configmap cluster-autoscaler-status -o yaml
Key Points
- Exit 137 with reason OOMKilled means the memory limit, not the node, killed it
- CFS quota throttling shows up as latency spikes, check cpu.stat
- Pod CIDR secondary range caps node count long before CPU does
- minAvailable equal to replica count deadlocks node upgrades
- Make runtimes cgroup-aware so heap sizing respects container limits
Q40A BigQuery query that used to finish in two minutes now runs for forty and sometimes fails on resources exceeded. How do you fix it?
AdvancedBigQuery Performance
Answer
The error 'Resources exceeded during query execution' almost always means a single worker had to hold too much data in memory, and the two usual causes are a skewed join or an unbounded window function. Start with the execution details: the query plan shows per-stage input and output rows, slot time consumed, and how much time each stage spent on wait, read, compute and write. A stage where the maximum worker time is far above the average is skew, meaning one key dominates, typically a null, an empty string or a sentinel like 'unknown' that matches millions of rows on both sides of a join.
The fix is to filter those keys out, salt them across buckets, or split the query into the skewed key handled separately and everything else. Window functions with an unbounded PARTITION BY, and ORDER BY on a huge result set, both force data onto one worker, so replace them with an aggregation plus a join where possible. Beyond skew: check that partition pruning still happens, because a filter that used to be a literal may now come from a joined subquery after a refactor, and pruning silently stops.
Ensure the smaller side of a join is genuinely small enough for a broadcast join, and consider materialising an intermediate result to a temporary table. Materialized views maintain incrementally refreshed aggregates and are automatically substituted into matching queries, which is the strongest fix for repeated dashboard aggregations. BI Engine reserves memory for sub-second dashboard serving. If none of that applies, check reservation contention through JOBS_TIMELINE: the SQL may be unchanged while the slots available to it collapsed because another team's job now shares the reservation.
-- 1. Find the skew: which key dominates the join?
SELECT merchant_id, COUNT(*) AS n
FROM `analytics.events`
WHERE event_date BETWEEN '2026-08-01' AND '2026-08-07'
GROUP BY merchant_id ORDER BY n DESC LIMIT 10;
-- 2. Inspect stage-level skew for a finished job
SELECT stage.name, stage.slot_ms,
stage.wait_ms_max, stage.wait_ms_avg,
stage.compute_ms_max, stage.compute_ms_avg,
stage.shuffle_output_bytes_spilled
FROM `region-asia-south1`.INFORMATION_SCHEMA.JOBS_BY_PROJECT j,
UNNEST(j.job_stages) AS stage
WHERE j.job_id = 'bquxjob_1a2b3c4d_1234567890ab'
ORDER BY stage.slot_ms DESC;
-- 3. Salt the hot key so it spreads across workers
SELECT e.merchant_id, COUNT(*) AS events
FROM (
SELECT *, IF(merchant_id = 'UNKNOWN', MOD(ABS(FARM_FINGERPRINT(user_id)), 64), 0) AS salt
FROM `analytics.events`
WHERE event_date BETWEEN '2026-08-01' AND '2026-08-07'
) e
JOIN `analytics.merchants` m
ON m.merchant_id = e.merchant_id
GROUP BY e.merchant_id;
-- 4. Precompute the dashboard aggregate once
CREATE MATERIALIZED VIEW `analytics.daily_gmv`
PARTITION BY event_date AS
SELECT event_date, merchant_id, SUM(amount_paise) AS gmv, COUNT(*) AS txns
FROM `analytics.events`
GROUP BY event_date, merchant_id;
Key Points
- Resources exceeded usually means join skew or an unbounded window function
- Compare compute_ms_max against compute_ms_avg per stage to prove skew
- Null and sentinel keys are the most common skew source in real data
- Materialized views are auto-substituted into matching dashboard queries
- Unchanged SQL that slowed down often means reservation contention
Q41Design a multi-region disaster recovery posture on GCP for a payments platform with an RPO of 5 minutes and an RTO of 30 minutes.
AdvancedDisaster Recovery
Answer
Start by fixing the vocabulary, because interviewers check it: RPO is how much data you can afford to lose, RTO is how long you can be down. Five minutes and thirty minutes rules out backup-and-restore and rules in warm standby. Pick asia-south1 as primary and asia-south2 as secondary so both stay in India for residency.
Stateless tier: build immutable images in Artifact Registry replicated to both regions, deploy the same Cloud Run services or GKE clusters in each, and front them with a global external Application Load Balancer whose backend service includes both regions, so failover is capacity-based and automatic for the compute layer. Data tier is where the RPO is won or lost. Cloud SQL with a cross-region read replica gives asynchronous replication with typically low seconds of lag, and promotion is a manual, one-way operation that takes minutes, which fits a 30-minute RTO if the runbook is rehearsed.
Spanner in a multi-region configuration gives synchronous replication and effectively zero RPO with no promotion step, at higher cost, and is the strongest answer for a ledger. Cloud Storage should be dual-region with turbo replication, which targets a 15-minute replication RPO for the objects covered. Pub/Sub is already global, but subscribers must exist in both regions.
Then the parts candidates forget: DNS TTLs low enough to matter, secrets replicated to both regions, Terraform that can build the secondary from scratch, quota pre-requested in the secondary region because you cannot scale into quota you do not have, and IAM and org policy identical on both sides. Finally, an untested DR plan is a document, not a capability, so schedule a quarterly game day that actually promotes the replica and measures real RTO.
Key Points
- RPO 5 min and RTO 30 min implies warm standby, not backup and restore
- Keep both regions in India (asia-south1 and asia-south2) for residency
- Cloud SQL cross-region replica promotion is manual and one-way
- Spanner multi-region removes the promotion step at higher cost
- Pre-request quota in the secondary region and rehearse with game days
Q42An Indian regulated customer asks you to guarantee data never leaves India. What do you actually configure on GCP?
AdvancedCompliance and Residency
Answer
Turn the promise into enforceable controls rather than a policy document. First, region selection: place every resource in asia-south1 (Mumbai) or asia-south2 (Delhi NCR), and for BigQuery create datasets with an explicit location, because a dataset created in the default US location cannot be moved, it must be recreated and copied. Second, enforce it with the Organization Policy constraint constraints/gcp.resourceLocations set to the Indian location groups at the organisation or folder node, so a well-meaning engineer physically cannot create a bucket in us-central1.
Third, encryption: default Google-managed keys are fine technically, but regulated customers usually want customer-managed encryption keys in Cloud KMS with the key ring created in the same Indian region, and the constraint constraints/gcp.restrictNonCmekServices to require CMEK on named services. Cloud EKM goes further by keeping key material in an external key manager outside Google entirely, and Key Access Justifications lets you see and deny reasons for administrative access. Fourth, egress controls: a VPC Service Controls perimeter prevents an authorised identity from copying a dataset to a project outside the boundary, which is the actual exfiltration path residency rules care about.
Fifth, be honest about the exceptions, because a good interviewer will probe them: some services are global by design (IAM metadata, Cloud DNS, billing data, certain support and logging control planes), Cloud Logging has its own regionalisation settings that you must configure separately, and support engineers may access systems from other regions unless you use Assured Workloads, which enforces personnel and location controls as a package. Under India's DPDP Act 2023 the obligations sit with the data fiduciary, so document these controls, the audit log evidence, and your breach notification path.
# 1. Hard-enforce Indian regions org-wide
cat > loc.yaml <<'EOF'
name: organizations/123456789012/policies/gcp.resourceLocations
spec:
rules:
- values:
allowedValues: [in:asia-south1-locations, in:asia-south2-locations]
EOF
gcloud org-policies set-policy loc.yaml
# 2. CMEK key ring in Mumbai, with rotation
gcloud kms keyrings create prod-in --location=asia-south1
gcloud kms keys create app-data --location=asia-south1 --keyring=prod-in \
--purpose=encryption --rotation-period=90d --next-rotation-time='2026-11-01T00:00:00Z'
# 3. Bucket and BigQuery dataset bound to that key and location
gcloud storage buckets create gs://acme-kyc-docs --location=asia-south1 \
--default-encryption-key=projects/my-prod-api/locations/asia-south1/keyRings/prod-in/cryptoKeys/app-data \
--uniform-bucket-level-access
bq --location=asia-south1 mk --dataset \
--default_kms_key=projects/my-prod-api/locations/asia-south1/keyRings/prod-in/cryptoKeys/app-data \
my-prod-api:kyc
# 4. Regionalise log storage too, it is not covered by resourceLocations alone
gcloud logging buckets create in-logs --location=asia-south1 --retention-days=400
Key Points
- BigQuery dataset location is immutable, choose asia-south1 at creation
- constraints/gcp.resourceLocations turns the promise into enforcement
- CMEK in a Mumbai key ring, or Cloud EKM for external key custody
- VPC Service Controls blocks the copy-to-outside-project exfiltration path
- Some control planes are global; Assured Workloads covers personnel controls
Q43How do you build end-to-end observability on GCP with OpenTelemetry, and how would you alert on an SLO instead of on CPU?
AdvancedObservability
Answer
Instrument once with OpenTelemetry and export to Google's backends: traces to Cloud Trace, metrics to Cloud Monitoring, and logs through the standard structured-logging path so they carry trace context. The critical detail is trace correlation. Cloud Run, GKE and the load balancer propagate the X-Cloud-Trace-Context header, and OpenTelemetry propagates traceparent under W3C Trace Context, so you configure a composite propagator that understands both, otherwise your application spans and the Google-generated front-end spans end up in different traces.
In your log lines, emit logging.googleapis.com/trace with the full projects/PROJECT/traces/TRACE_ID format and logging.googleapis.com/spanId, and Cloud Logging will link every log entry to its trace in the console, which is the single highest-value ten minutes of setup on any GCP service. For metrics, Google Cloud Managed Service for Prometheus ingests Prometheus metrics at scale and lets you query with PromQL alongside Cloud Monitoring metrics, so you do not have to run and shard your own Prometheus. On alerting, the mature answer is service level objectives rather than resource thresholds.
Define an SLI (for example the ratio of requests with latency under 300 ms, or the ratio of non-5xx responses), set an SLO such as 99.9% over a rolling 28 days, and alert on error budget burn rate: a fast-burn alert like 14.4 times budget consumption over one hour pages someone, and a slow-burn alert like 3 times over six hours opens a ticket. This eliminates the CPU-at-80% alert that pages at 2 AM for a service that is serving users perfectly well, and it is exactly the answer senior SRE interviewers at product companies are listening for.
// OpenTelemetry with both propagators so LB and app spans join up
// import { NodeSDK } from '@opentelemetry/sdk-node';
// import { TraceExporter } from '@google-cloud/opentelemetry-cloud-trace-exporter';
// import { CompositePropagator, W3CTraceContextPropagator } from '@opentelemetry/core';
// import { CloudPropagator } from '@google-cloud/opentelemetry-cloud-trace-propagator';
//
// const sdk = new NodeSDK({
// traceExporter: new TraceExporter(),
// textMapPropagator: new CompositePropagator({
// propagators: [new W3CTraceContextPropagator(), new CloudPropagator()],
// }),
// });
// sdk.start();
// Structured log that links itself to the trace
// console.log(JSON.stringify({
// severity: 'ERROR',
// message: 'payment capture failed',
// 'logging.googleapis.com/trace': `projects/my-prod-api/traces/${traceId}`,
// 'logging.googleapis.com/spanId': spanId,
// orderId,
// }));
# Availability SLO on a Cloud Run service, then burn-rate alerting
gcloud alpha monitoring slos create \
--service=orders-api \
--slo-id=availability-999 \
--goal=0.999 --rolling-period=28d \
--request-based-good-total-ratio \
--display-name='99.9% non-5xx over 28 days'
Key Points
- Use a composite propagator for W3C traceparent and X-Cloud-Trace-Context
- Emit logging.googleapis.com/trace to link logs to traces automatically
- Managed Service for Prometheus gives PromQL without running Prometheus
- Alert on error budget burn rate, not on CPU or memory thresholds
- Fast burn pages, slow burn tickets, everything else stays a dashboard
Q44You are migrating a mid-size AWS estate to GCP. What differs architecturally, and how do you design the landing zone?
AdvancedMigration and Architecture
Answer
The mental model shifts in four places. Accounts become projects, and because projects are cheap and disposable, the GCP idiom is many small projects grouped into folders rather than a handful of large accounts; the AWS Organizations to folder mapping is the first design conversation. Networking is the biggest change: a GCP VPC is global, so the transit gateway and inter-region peering mesh you built on AWS mostly disappears, replaced by one Shared VPC in a host project with subnets per region and service projects attached.
Firewall rules replace security groups and NACLs and are evaluated by priority with implied rules at the bottom, and targeting by service account is the closer analogue to a security group than network tags are. IAM is coarser in a good way: roles are granted to principals on resources within a hierarchy that inherits, there are no inline policies or trust relationships to reason about, and cross-account assume-role becomes service account impersonation or Workload Identity Federation. Data services map roughly (RDS to Cloud SQL, DynamoDB to Firestore or Bigtable, Redshift to BigQuery, Kinesis to Pub/Sub, EMR to Dataproc, Lambda to Cloud Run functions), but BigQuery is a genuinely different beast from Redshift and usually reshapes the data platform rather than being a like-for-like swap.
For the landing zone, build in this order: organisation, folder structure, org policies including resource locations and key restrictions, a Shared VPC host project with planned CIDRs, centralised logging and billing export projects, Terraform state and a CI identity on Workload Identity Federation, then the first workload project. Migrate with Migrate to Virtual Machines for lift-and-shift, Database Migration Service for Cloud SQL, and Storage Transfer Service for S3 to Cloud Storage, and model the AWS data egress cost of the transfer explicitly, because it is often the largest single line item in the whole migration.
Key Points
- AWS accounts map to projects, but projects are cheap so you create many more
- Global VPC and Shared VPC replace most transit gateway topology
- Firewall rules target service accounts, the closest thing to security groups
- Cross-account assume-role becomes impersonation or Workload Identity Federation
- Sequence: org, folders, policies, Shared VPC, logging, Terraform, then workloads
Q45How would you manage twenty GKE clusters across regions and teams without configuration drift?
AdvancedMulti-cluster Platform
Answer
At that count, the answer is fleet management rather than cluster management. A fleet is a group of clusters registered to a host project, and registration gives you fleet-wide identity (a shared workload identity pool so a Kubernetes service account name means the same thing in every cluster), fleet-scoped namespaces, and a single place to apply features. Config Sync provides GitOps: each cluster reconciles continuously against a Git repository, so the repository is the source of truth and manual kubectl edits are reverted automatically, which is the actual mechanism that stops drift.
Structure the repo with a shared base plus per-cluster and per-team overlays using Kustomize, and use a root sync for platform-level resources that teams cannot override plus repo syncs scoped to their own namespaces. Policy Controller, built on Open Policy Agent Gatekeeper, enforces constraints at admission across the fleet: required labels, no privileged containers, only images from your Artifact Registry repository, mandatory resource requests, and it can run in dry-run mode so you can measure violations before enforcing. Multi Cluster Ingress or the multi-cluster Gateway API distributes traffic across clusters from a single anycast IP with automatic failover, and multi-cluster Services lets a service in one cluster resolve and reach a service in another.
Layer on Binary Authorization so only attested images run, release channels plus maintenance windows so upgrades are predictable, and a policy that clusters are cattle: you should be able to delete and rebuild any cluster from Terraform plus Git within an hour. The failure mode to plan for is the sync itself, because a bad commit now propagates to twenty clusters at once, so treat the config repo with the same review, staging and progressive rollout discipline as application code.
# Register clusters into a fleet
gcloud container fleet memberships register prod-mum \
--gke-cluster=asia-south1/prod-mum --enable-workload-identity
gcloud container fleet memberships register prod-del \
--gke-cluster=asia-south2/prod-del --enable-workload-identity
# Turn on Config Sync fleet-wide
gcloud container fleet config-management enable
cat > cm.yaml <<'EOF'
applySpecVersion: 1
spec:
configSync:
enabled: true
sourceFormat: unstructured
syncRepo: https://github.com/acme/gke-config
syncBranch: main
policyDir: clusters/prod
secretType: gcpserviceaccount
gcpServiceAccountEmail: config-sync@my-prod-api.iam.gserviceaccount.com
policyController:
enabled: true
referentialRulesEnabled: true
auditIntervalSeconds: 60
EOF
gcloud container fleet config-management apply --membership=prod-mum --config=cm.yaml
# Confirm every cluster is actually in sync
gcloud container fleet config-management status
Key Points
- Fleets give shared identity, fleet namespaces and fleet-wide features
- Config Sync reverts manual kubectl drift by reconciling against Git
- Policy Controller (OPA Gatekeeper) enforces admission rules fleet-wide
- Multi-cluster Gateway distributes traffic with cross-cluster failover
- A bad config commit now hits every cluster, so stage rollouts of the repo too
Frequently Asked Questions
What salary can a GCP engineer expect in India in 2026?
Roughly ₹8-28 LPA depending on level and depth. Freshers and one-to-two-year engineers with the Associate Cloud Engineer certification and some hands-on Terraform typically land ₹5-9 LPA at IT services firms and ₹9-14 LPA at product companies. Mid-level cloud or DevOps engineers with three to six years, real GKE and CI/CD ownership, sit around ₹14-24 LPA. Cloud architects and platform leads with Professional Cloud Architect plus multi-region and cost-governance experience go past ₹28 LPA, and data engineers whose BigQuery and Dataflow work is genuinely deep often earn more than generalist cloud engineers at the same experience level.
How long does it take to prepare for a GCP interview?
If you already work on AWS or Azure, four to six weeks of focused effort is enough, because the concepts transfer and you are mainly relearning names, the global VPC model and the IAM hierarchy. From scratch, plan three to four months: one month on IAM, networking, compute and storage with everything built by hand in a free-tier project, one month on Cloud Run, GKE and Cloud SQL with a real application deployed through a pipeline, and one month on BigQuery, Pub/Sub, Terraform and observability. Build and destroy things yourself; interviewers can tell within two questions whether you have only watched videos.
Are GCP certifications worth it, and which one should I take?
They help most at the shortlisting stage, particularly at IT services companies and Google Cloud partners where certification counts toward partner tier, which means recruiters actively filter for them. Start with Associate Cloud Engineer if you are early career, then Professional Cloud Architect if you are heading toward design and pre-sales work, Professional Cloud DevOps Engineer if you live in CI/CD and SRE, or Professional Data Engineer if BigQuery and Dataflow are your day job. At product companies the certificate opens the door but never closes the deal; the technical round is still about whether you can debug a real cluster.
Should I learn GCP or AWS first in the Indian job market?
AWS still has the larger number of open roles in India, so if pure job volume is your only criterion, start there. GCP roles are fewer but the competition per role is lower, salaries are comparable or slightly better at the senior end, and demand is concentrated in data-heavy and Kubernetes-heavy teams which tend to be the more interesting engineering environments. The practical answer for most people is to go deep on one, ship real systems, and then learn the second in a few weeks, because the underlying distributed systems reasoning is what actually transfers between them.
Can a fresher get a GCP job, or is cloud only for experienced engineers?
Freshers do get hired, mostly into cloud support, cloud operations and junior DevOps roles at IT services companies, GCP partners and mid-size product firms. What differentiates a hired fresher is a public portfolio rather than a certificate: a Terraform repository that stands up a VPC, a Cloud Run service, a Cloud SQL instance and a monitoring dashboard, plus a written explanation of the cost model. Add a small BigQuery project with partitioned tables and a scheduled query and you will interview better than most two-year candidates who have only clicked through the console.
Is GCP knowledge still valuable when everything is moving to Kubernetes and Terraform?
Kubernetes and Terraform are how you express infrastructure, but every real cluster still runs on a provider whose IAM model, networking, quotas, load balancers and failure modes you have to understand. On GCP that means GKE's pod CIDR planning, Workload Identity Federation, Shared VPC, and BigQuery, none of which Terraform abstracts away. The engineers who struggle in 2026 interviews are the ones who can write HCL but cannot explain why their pods lost IPs or why their Cloud Run service cannot reach Memorystore. Provider depth is the differentiator, not a substitute for the tooling.
Introduction
Google Cloud is the third-largest public cloud but the fastest-growing of the big three in India, and the shape of a GCP interview reflects that. Fewer questions about decade-old services, far more about BigQuery, GKE and Cloud Run. Google Cloud operates two Indian regions, asia-south1 in Mumbai and asia-south2 in Delhi NCR, and demand for engineers who can design inside them arrives from three directions at once: product companies that standardised on GCP for data and ML workloads, consumer-internet and telecom firms with existing Google partnerships, and the large IT services players staffing Google Cloud practices for global clients out of Bengaluru, Pune and Hyderabad.
Interviews in 2026 rarely stop at naming services. Expect to be asked how IAM allow policies inherit down the resource hierarchy and why removing a project-level binding does not revoke an organisation-level grant, why a Cloud Run revision fails with 'Container failed to start and listen on the port defined by the PORT environment variable', how BigQuery bills bytes scanned and what partition pruning actually does to that number, and how a GKE node pool runs out of pod IP addresses while sitting at 30% CPU. Architect rounds add cost modelling, private connectivity design, and a whiteboard exercise on surviving the loss of a single zone.
This set works through 45 questions in roughly the order interviewers travel: 18 fundamentals covering hierarchy, IAM, networking, storage classes and compute selection, 18 intermediate questions on Workload Identity Federation, Cloud Run scaling, BigQuery partitioning, Pub/Sub delivery semantics, Terraform and cost control, and 9 advanced questions on production failure modes, multi-region disaster recovery, data residency under India's DPDP Act, and observability. Most answers carry a gcloud command, Terraform block, SQL query or YAML manifest you can actually run, because GCP rounds increasingly happen in a shared terminal rather than on a slide.
Ready to practice Google Cloud Platform interviews?
Don't just read, practice these Google Cloud Platform questions live with an AI interviewer that asks follow-ups and scores your answers.