VPC Network Peering connects two Google Cloud VPC networks so that resources in each can reach the other over internal IP addresses. The networks can be in the same project, different projects or different organisations. There is no gateway VM, VPN tunnel or appliance in the path: once both sides agree, each network gains routes to the other's subnets and traffic stays on Google's network with the same characteristics as traffic inside one VPC.
That simplicity hides several rules that shape whole network designs: peering is not transitive, subnet ranges may not overlap, firewall rules and internal DNS do not cross the link, and only some route types are exchanged. This article explains each rule from the routing model up, walks through a full setup with gcloud, covers private services access (the peering behind Cloud SQL private IP and similar services), and ends with failure modes and a design checklist. For peering as a concept across clouds, see VPC peering architecture; here the focus is Google Cloud's specific behaviour.
The routing model
A Google Cloud VPC is a global network with regional subnets, and its routing table holds subnet routes, static routes and dynamic routes learned by Cloud Router. A peering is a pair of configurations, one created in each network, each naming the other. Each side is configured independently, and the peering only becomes ACTIVE when both exist and match. Until then the state is INACTIVE and no routes move. Either administrator can end the relationship by deleting their side.
When the peering is active, each network imports routes from the other according to these rules, taken from Google's current documentation:
| Route type | Exchanged? | Control |
|---|---|---|
| IPv4 subnet routes, private ranges (primary and secondary) | Always, both directions | Cannot be disabled |
| IPv4 subnet routes using privately used public ranges | Exported by default; import is opt-in | --export-subnet-routes-with-public-ip, --import-subnet-routes-with-public-ip |
| IPv6 subnet routes | Only when enabled | --stack-type=IPV4_IPV6 |
| Static routes (no tags, not via default internet gateway) | Only when enabled | --export-custom-routes / --import-custom-routes |
| Dynamic routes from Cloud Router | Only when enabled | Same custom-route flags |
| Tagged static routes, default internet gateway routes, policy-based routes | Never | - |
Two consequences follow immediately. Because private subnet routes are always exchanged, peering exposes every subnet in both networks to each other at the routing layer; the only way to narrow what can talk to what is firewall policy. And because default-route and tagged routes are never exchanged, a peer cannot use your internet egress path, your NAT instances or your Cloud NAT gateway.
Non-transitivity and what to do about it
Peering does not provide transitive routing. If A peers with B and A peers with C, B and C cannot reach each other through A, even with custom route export enabled, because routes imported over one peering are not re-exported over another. Hub-and-spoke designs built only from peerings therefore give each spoke access to the hub and nothing else.
That is sometimes exactly what you want: a shared-services VPC that every team reaches while teams stay isolated from each other. When spokes must talk, the options are a full mesh of peerings (which grows as n(n-1)/2 links and runs into quotas), Shared VPC for teams in one organisation (see Shared VPC), Network Connectivity Center with VPC spokes (see Network Connectivity Center), or HA VPN between VPCs, where Cloud Router can advertise custom ranges and so carry routes on.
There is one important partial exception: dynamic routes. If VPC A learns on-premises prefixes through Cloud Router over VPN or Interconnect, and the A-B peering has export on A and import on B, then B can reach on-prem through A's hybrid link. B's own subnets still have to be advertised to on-prem by A's Cloud Router as custom advertisements, because Cloud Router does not automatically advertise peer subnets.
Address planning and peering-group quotas
Peering refuses any configuration in which a subnet range in one network exactly matches, contains or fits within a subnet range in the peer. The check applies at creation and whenever a subnet is added or expanded later, so a team that creates an overlapping subnet in a peered network will have the operation rejected. Plan address space centrally: allocate each VPC a non-overlapping block, include secondary ranges used by GKE pods and services, and reserve ranges for private services access. Retrofitting non-overlapping ranges into existing networks is one of the most expensive network migrations a team can face.
Quotas are counted per peering group, which is a network plus every network directly peered to it. Subnet ranges, static routes and dynamic routes across the whole group count toward limits that apply to the group, not to one network, so a busy hub can exhaust a limit because of what its spokes contain. Check the current values on the VPC quotas page and in your project before committing to a large hub; limits change over time.
Worked example: peering two projects
The following sets up peering between net-a in proj-a and net-b in proj-b, with custom route exchange so that B can use A's hybrid connectivity. Each command is run by an administrator of the respective project.
# In proj-a: create A's side, exporting A's static and dynamic routes.
gcloud compute networks peerings create a-to-b \
--project=proj-a --network=net-a \
--peer-project=proj-b --peer-network=net-b \
--export-custom-routes
# In proj-b: create B's side, importing them. The peering turns ACTIVE now.
gcloud compute networks peerings create b-to-a \
--project=proj-b --network=net-b \
--peer-project=proj-a --peer-network=net-a \
--import-custom-routes
# Verify state and the routes actually exchanged.
gcloud compute networks peerings list --project=proj-b --network=net-b
gcloud compute networks peerings list-routes b-to-a \
--project=proj-b --network=net-b --region=europe-west1 --direction=INCOMINGFirewall rules do not cross the peering, so B must allow ingress from A's ranges explicitly. Rules that use network tags or service accounts identify instances in the local network only, so for traffic from the peer, match on source IP ranges.
gcloud compute firewall-rules create allow-from-net-a \
--project=proj-b --network=net-b --direction=INGRESS \
--source-ranges=10.10.0.0/16 --allow=tcp:443,tcp:5432 \
--target-tags=api-backendCompute Engine internal DNS names are not resolvable across peering either. To let B resolve names that A hosts in Cloud DNS private zones, create a DNS peering zone in B that targets A's network:
gcloud dns managed-zones create corp-from-a \
--project=proj-b --dns-name=corp.internal. \
--visibility=private --networks=net-b \
--target-project=proj-a --target-network=net-a \
--description="Resolve corp.internal using net-a's DNS view"Clients in B can also reach internal load balancers in A over the peering, which is the usual way to publish a shared service. Check the specific load balancer type's documentation for global-access requirements when clients are in other regions.
Consensus mode for coordinated changes
By default, each side changes its own configuration independently: if one administrator deletes their side or disables route export, connectivity changes immediately, which has caused outages between teams that did not coordinate. Google Cloud offers a consensus update strategy in which changes need acknowledgement from the peer. In consensus mode a deletion becomes a request that the peer must also make, and updates show statuses such as PENDING_PEER_ACKNOWLEDGMENT and IN_SYNC.
gcloud compute networks peerings update a-to-b \
--project=proj-a --network=net-a --update-strategy=CONSENSUS
# Later, deletion is a two-sided request rather than an immediate cut.
gcloud compute networks peerings request-delete a-to-b --project=proj-a --network=net-aAvailability of this flag has depended on the gcloud release track; if your installed version rejects it, check gcloud beta compute networks peerings update --help and the current documentation. For peerings between organisations or teams with separate on-call rotations, consensus mode is worth the extra step.
Private services access is peering too
Private services access is how Cloud SQL, Memorystore and several other managed services receive private IPs in your network. You reserve an internal range, then create a peering to a Google-managed producer network through the Service Networking API; the service's instances get addresses from your reserved range inside the producer network.
gcloud compute addresses create psa-range --project=proj-a --global \
--purpose=VPC_PEERING --addresses=10.50.0.0 --prefix-length=20 --network=net-a
gcloud services vpc-peerings connect --project=proj-a \
--service=servicenetworking.googleapis.com --ranges=psa-range --network=net-aBecause this is ordinary VPC peering, the same rules apply, and they explain the most common support question: "Why can on-premises clients not reach my Cloud SQL private IP?" Two things are needed. The producer network must learn the on-prem routes, so enable --export-custom-routes on your side of the peering named servicenetworking-googleapis-com. And on-prem must learn the reserved range, so add it as a custom advertisement on the Cloud Router that serves the VPN or Interconnect, because peered ranges are not advertised automatically. Non-transitivity also means a second VPC peered to net-a cannot reach the managed service through net-a; consider Private Service Connect where the service supports it.
Failure modes
| Symptom | Cause | Fix |
|---|---|---|
Peering stuck INACTIVE | Only one side created, or names do not match | Create or correct the peer's side |
| Subnet creation fails in a peered VPC | New range overlaps a peer's range | Choose a non-overlapping range |
| Spoke B cannot reach spoke C | Peering is not transitive | NCC, Shared VPC, direct peering or HA VPN |
| Peer cannot reach the internet through you | Default and tagged routes never exchanged | Give the peer its own Cloud NAT |
| Packets dropped after routes look right | No ingress firewall rule for peer ranges | Allow peer source ranges explicitly |
| Names fail to resolve across the link | Internal DNS not shared | DNS peering zone or shared private zone |
| On-prem cannot reach Cloud SQL private IP | Missing export on the PSA peering or missing advertisement | Export custom routes; advertise the range |
When a path fails, debug in layers rather than guessing. First confirm the peering state on both sides. Then list the imported routes with list-routes and check the destination prefix is present. Next run a Connectivity Test from Network Intelligence Center between the two endpoints; it evaluates routes and firewall rules and names the rule or missing route that blocks the packet. Finally, enable VPC Flow Logs on the relevant subnets to see whether traffic arrives at all. Peering has no hourly charge of its own, but traffic across it is billed like other internal traffic between zones and regions, so check the current pricing page for cross-region designs.
Trade-offs and alternatives
Peering is the cheapest and simplest way to connect a handful of VPCs: no extra hops, no gateways to size and no per-tunnel bandwidth limit. Its costs are architectural. Every subnet is exposed at the routing layer, so segmentation depends entirely on firewall discipline. Address space must be coordinated across every network that will ever be peered. Transitivity has to come from somewhere else. And each side can change the relationship unilaterally unless consensus mode is used. If you want central control and one address plan, Shared VPC fits better; if you want many networks to reach each other, Network Connectivity Center fits better; if you want to expose one service without merging networks, Private Service Connect is usually the safest choice. Combine peering with VPC Service Controls when the concern is data exfiltration through Google APIs rather than packet routing, and see Google Cloud VPC fundamentals for the routing model underneath.
What to do next
- Draw an address plan that covers every VPC, GKE secondary range and private services access range.
- Decide per relationship whether you need peering, Shared VPC, NCC or Private Service Connect.
- Create both peering sides and confirm the routes with list-routes in both directions.
- Write ingress firewall rules for peer source ranges; do not rely on peer tags or service accounts.
- Set up DNS peering zones for names that must resolve across the link.
- Enable consensus mode for peerings between separately managed teams.
- For private services access with hybrid clients, export custom routes and advertise the reserved range.
- Review peering-group quotas before adding more spokes to a hub.