Chapter 9: Building Real GCP Infrastructure
Topic 56

Global VPC and Regional Subnets

NetworkingVPC

On GCP a VPC is a global resource and its subnets are regional — the exact inverse of what an AWS engineer expects, where the VPC is regional and subnets are pinned to an availability zone. One google_compute_network for Hatch spans every region on Earth; you carve a google_compute_subnetwork in us-central1 out of it, and a VM in any zone of that region attaches to the same subnet with no AZ-to-subnet mapping to design around.

This single inversion is the source of most AWS-brain mistakes on GCP networking. If you replicate the one-subnet-per-AZ habit, you fragment your address space for no redundancy benefit and build a design that fights the platform. Internalize that the network is global and the subnet is regional, and the rest of GCP networking — Shared VPC, firewalls, private connectivity — falls into place.

The Network Is Global

A google_compute_network has no region. It is one routing domain that reaches every GCP region, so a VM in us-central1 and one in europe-west1 attached to the same VPC talk over Google's backbone using internal IPs, with no peering and no transit gateway between them. The VPC is the boundary; the regions are just where you place subnets inside it.

For Hatch, that means hatch-net-host holds exactly one global VPC. You do not create a VPC per region and stitch them together — there is one network object, and you add a regional subnet wherever you want to place workloads. The configuration below declares the host network in custom mode, which the next section explains.

A custom-mode global VPC
resource "google_compute_network" "hatch" {
  project                 = "hatch-net-host"
  name                    = "hatch-vpc"
  auto_create_subnetworks = false   # declare every subnet explicitly
  routing_mode            = "GLOBAL"  # dynamic routes propagate across regions
}

Subnets Are Regional, Not Zonal

A google_compute_subnetwork belongs to exactly one region and carries a primary CIDR — say 10.10.0.0/20 for the Hatch app subnet in us-central1. Every zone in that region draws addresses from that one subnet. There is no per-AZ subnet to replicate, and no "which subnet do I use for zone -a versus -c" decision, because the zones share the regional subnet.

This is exactly where the AWS habit hurts. In AWS you build a subnet per availability zone and spread instances across them for resilience. On GCP that resilience is automatic within a regional resource — a regional managed instance group already spans the zones of us-central1 on the single subnet — so three hand-made "zonal" subnets just chop your /20 into fragments with no benefit.

One regional subnet for the whole region
resource "google_compute_subnetwork" "app" {
  project       = "hatch-net-host"
  name          = "app-uc1"
  region        = "us-central1"     # regional, not zonal
  network       = google_compute_network.hatch.id
  ip_cidr_range = "10.10.0.0/20"     # every zone of us-central1 shares this
}

Auto Mode vs Custom Mode

Setting auto_create_subnetworks = true creates an auto-mode VPC: Google spawns a /20 subnet in every region automatically, from a fixed default address plan. That is fine for a throwaway sandbox where you never peer or connect to on-prem. It is wrong for anything real, because those default ranges are prone to collide the moment you peer two VPCs or attach a VPN to a corporate network — and you cannot renumber an auto-mode VPC without recreating it.

For hatch-net-host you set auto_create_subnetworks = false and declare each subnet explicitly. Custom mode is the only sane choice for a network you intend to govern: you own the address plan, you place subnets only where you need them, and nothing collides because nothing was auto-generated behind your back.

Secondary IP Ranges

A subnet can carry named secondary_ip_range blocks beyond its primary CIDR. This is how GKE gets alias-IP ranges: a pods range and a services range so containers receive real, routable VPC addresses instead of being hidden behind NAT. The pods get genuine internal IPs, which is what makes GKE networking on GCP feel native rather than bolted on.

Size these up front, because a secondary range is effectively fixed once a cluster consumes it. A too-small /24 pods range caps the cluster at a few hundred pods forever, and growing it later is a rebuild. Plan a generous /16-class pods range and a smaller services range before the cluster exists, not after it hits the ceiling.

Primary plus secondary ranges for GKE
resource "google_compute_subnetwork" "gke" {
  project       = "hatch-net-host"
  name          = "gke-uc1"
  region        = "us-central1"
  network       = google_compute_network.hatch.id
  ip_cidr_range = "10.20.0.0/20"     # nodes

  secondary_ip_range {
    range_name    = "pods"
    ip_cidr_range = "10.96.0.0/14"   # sized for pod density, fixed once used
  }
  secondary_ip_range {
    range_name    = "services"
    ip_cidr_range = "10.100.0.0/20"
  }
}

Internal Ranges and Routing

The VPC has implied routes between all of its subnets, so subnets in different regions reach each other internally with no configuration. You size the primary range for the VMs you expect and the secondary ranges for the pod and service density you plan, and the routing between them is handled for you.

The discipline that matters is address planning. Overlapping the primary and a secondary range, or two subnets' ranges, breaks alias IPs or peering silently — the VPC accepts the config and the failure only surfaces when GKE or a peer tries to use the colliding range. Lay out non-overlapping CIDRs deliberately and write them down next to the code, because the cost of getting it wrong is a renumber or a rebuild.

GCP network scopes
VPC — global
One google_compute_network spans every region. Cross-region traffic stays internal with no peering.
Subnet — regional
A google_compute_subnetwork lives in one region; every zone of that region shares it.
Secondary ranges
Named alias-IP ranges on a subnet — how GKE pods and services get routable VPC addresses.
GCP global VPC vs AWS regional VPC

GCP VPC — one global object. Its subnets are regional, every zone shares a subnet, and cross-region internal traffic needs no peering. You design one network and place regional subnets inside it.

AWS VPC — regional. Its subnets are pinned to one AZ, you replicate a subnet per AZ, and cross-region traffic needs VPC peering or Transit Gateway. Carrying the AWS one-subnet-per-AZ habit onto GCP produces redundant subnets and a network design that fights the platform.

Common Mistakes
  • Creating a subnet per zone out of AWS habit — GCP subnets are regional, so three "zonal" subnets in us-central1 just fragment your address space for no redundancy benefit.
  • Leaving auto_create_subnetworks = true on a real VPC and getting Google's default /20 in every region, which collides the moment you peer or connect to on-prem and cannot be renumbered without recreating.
  • Sizing a GKE secondary pod range as a /24 and capping the cluster at a few hundred pods forever — the secondary range is fixed once the cluster uses it, so undersizing is a rebuild.
  • Assuming two regions need peering to talk — the same global VPC already routes between regional subnets internally, and adding peering on top is wasted complexity.
  • Overlapping the primary and a secondary range, or two subnets' ranges, so alias IPs or peering silently break — the VPC accepts the config and the failure shows up only when GKE or a peer tries to use the range.
Best Practices
  • Set auto_create_subnetworks = false and declare every subnet explicitly for hatch-net-host, so address space is intentional and peer-safe.
  • Plan secondary ranges for GKE up front — a generous /16-class pods range and a smaller services range — because they are fixed once the cluster consumes them.
  • Use one global VPC across regions and rely on its internal routing instead of peering same-org regions together.
  • Document the primary and secondary CIDRs in code comments next to each subnet so the next engineer sees the address plan without spelunking.
  • Lay out non-overlapping CIDRs deliberately across the whole VPC before creating subnets, so peering and alias IPs never collide later.
Comparable tools AWS aws_vpc / aws_subnet — regional VPC, zonal subnets, the contrasting model Config Connector ComputeNetwork / ComputeSubnetwork gcloud compute networks subnets create — the imperative form

Knowledge Check

Why does a single Hatch VPC reach both us-central1 and europe-west1 with no peering?

  • A GCP VPC is a global resource and routes internally between its regional subnets
  • Terraform creates a hidden peering connection between the two regions automatically
  • The two regions share an availability zone that bridges them
  • GCP tunnels the traffic over the public internet between regions

An engineer builds three subnets in us-central1, one per zone, the way they would in AWS. What is the result on GCP?

  • Wasted, fragmented address space — GCP subnets are regional, so one subnet already serves every zone
  • Improved resilience, because each zone now has a dedicated subnet as its own failure domain, isolated from a routing fault in the others
  • A required configuration; GCP rejects a region with fewer than one subnet per zone
  • Faster cross-zone traffic, since each zone routes through its own subnet

Why do you size a GKE secondary pod range generously before the cluster exists?

  • The secondary range is effectively fixed once the cluster uses it, so an undersized range caps pods and forces a rebuild
  • Larger ranges make pod-to-pod traffic faster on Google's backbone, since a roomier CIDR lets the scheduler pack pods onto closer nodes
  • GKE bills per unused address, so a big range is cheaper
  • The pod range must always exactly equal the primary node range

When is auto_create_subnetworks = true the wrong choice?

  • For any VPC you will peer or connect to on-prem, because the default ranges collide and cannot be renumbered without recreating
  • For a throwaway sandbox that never connects to another network
  • Only when the VPC has more than one secondary range
  • It is never wrong; auto mode is Google's recommended default for production because it lays out non-overlapping ranges in every region for you

You got correct