Every Google Cloud project can own its own VPC network, and in a small company that is fine. In a company with forty teams it produces forty networks with overlapping IP ranges, forty sets of firewall rules nobody reviews, and a peering mesh that runs into limits the week someone needs on-premises connectivity. Shared VPC is Google Cloud's answer: one project owns the network, and many other projects place their VMs, GKE clusters and databases on its subnets while keeping their own budgets, quotas and IAM.
The VPC fundamentals article introduces Shared VPC in one section. This article is the long version. It explains the host and service project model from first principles, the exact IAM seam that decides who can do what, how GKE and managed services fit, how to plan IP space and firewall ownership, what breaks in practice, and how to roll it out without a flag day. Every role name and command below was checked against Google's Shared VPC documentation on 2026-10-03.
Host projects, service projects and who pays
Shared VPC has exactly two project roles. A host project contains one or more VPC networks that it shares. A service project is attached to a host project and is allowed to create resources whose network interfaces sit on the host's subnets. Three rules from Google's documentation shape every design decision. First, each service project can attach to only one host project at a time. Second, a project cannot be a host and a service project at once, so there is no chaining. Third, billing for a resource is attributed to the service project where the resource lives, even though its interface is in the host project's network.
That third rule is the point of the whole feature. Teams keep their own projects, budgets, quotas and IAM for compute, while a central network team owns subnets, routes, firewalls, NAT and hybrid connectivity in one place. Contrast this with VPC Network Peering, where every team owns a separate network and the networks exchange routes: peering keeps network administration distributed, does not let you centralise firewall rules, and is not transitive. Shared VPC is centralised by design.
A VM in a service project is an ordinary VM in that project. Its disks, service account, metadata and IAM bindings are all there. Only the network interface references a subnet by its full path in another project, for example projects/net-prod/regions/europe-west1/subnetworks/apps-euw1. Existing resources do not move onto the shared network when a project is attached; you create new ones, which matters for migration planning later.
The IAM seam
Shared VPC is mostly an IAM design. Three groups of people matter, and each needs a specific, narrow set of permissions.
| Persona | Role | Granted where | What it allows |
|---|---|---|---|
| Shared VPC Admin | roles/compute.xpnAdmin and roles/resourcemanager.projectIamAdmin | Organization or folder | Enable host projects, attach service projects, delegate subnet access |
| Network Admin / Security Admin | roles/compute.networkAdmin, roles/compute.securityAdmin | Host project | Create subnets, routes, NAT; create and change firewall rules |
| Service Project Admin | roles/compute.networkUser | Host project or individual subnets | Attach interfaces of new resources to the shared subnets |
The Network User role is the seam. Granted at the host project level, it lets a service project use every current and future subnet in the host. Granted on individual subnets, it lets a team use only the subnets you name. Subnet-level grants are almost always what you want in production: the payments team gets apps-euw1 and nothing else, and a typo in their Terraform cannot place a VM on the subnet reserved for the PCI zone.
Humans are not the only principals that create resources. Managed instance groups create VMs using the Google APIs service agent of the service project, SERVICE_PROJECT_NUMBER@cloudservices.gserviceaccount.com, so that account also needs Network User on the subnets the group uses. Forgetting this is the most common reason a MIG works in a standalone project and fails after moving to Shared VPC. GKE has its own service agent with its own requirements, covered below. The rule of thumb is simple: whatever identity actually calls the Compute API to create the interface needs Network User on that subnet.
Two organization policy constraints add guardrails above IAM. constraints/compute.restrictSharedVpcHostProjects limits which host projects a service project under a folder may attach to, and constraints/compute.restrictSharedVpcSubnetworks limits which subnets resources may use. IAM says who may act; these constraints say what is allowed even if IAM is too broad, which is the right layering for regulated environments.
Setting it up
Provisioning is three commands once the roles are in place. Enabling a project as a host also places a lien on it, a lock that prevents the project from being deleted while it is configured for Shared VPC; Google removes the lien when the project stops being a host. That lien has saved more than one organization from a cleanup script.
# 1. As Shared VPC Admin: make net-prod a host project (adds a lien)
gcloud compute shared-vpc enable net-prod
# 2. Attach a service project to it
gcloud compute shared-vpc associated-projects add payments-prod \
--host-project net-prod
# 3. Grant subnet-level Network User to the team and the MIG service agent
cat > subnet-policy.json <<'EOF'
{
"bindings": [{
"role": "roles/compute.networkUser",
"members": [
"group:payments-eng@example.com",
"serviceAccount:123456789012@cloudservices.gserviceaccount.com"
]
}]
}
EOF
gcloud compute networks subnets set-iam-policy apps-euw1 subnet-policy.json \
--region europe-west1 --project net-prod
# A service-project engineer then creates a VM on the shared subnet
gcloud compute instances create pay-api-1 --project payments-prod \
--zone europe-west1-b \
--subnet projects/net-prod/regions/europe-west1/subnetworks/apps-euw1 \
--no-addressNote that set-iam-policy replaces the subnet's whole policy. In real pipelines manage these bindings in Terraform or use add-iam-policy-binding so one team's change does not silently remove another's access. The final command shows the only visible difference for application teams: the --subnet flag takes a full resource path into the host project.
GKE and managed services on a shared network
GKE is where Shared VPC gets demanding, because a cluster does not just attach interfaces: it needs secondary ranges for Pods and Services, and it wants to create firewall rules and load-balancer resources as workloads change. A VPC-native cluster in a service project uses a host subnet as its node range and two secondary ranges on that subnet for Pods and Services. Plan those secondary ranges in the host project ahead of time; the Pod range in particular is large, because each node reserves a block of Pod addresses.
The GKE service agent of the service project, service-SERVICE_PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com, needs Network User on the cluster's subnet and the Host Service Agent User role, roles/container.hostServiceAgentUser, on the host project. Node pools are managed instance groups, so the project's Google APIs service agent needs Network User on that subnet too. Firewall rules are the second friction point. They live in the host project, so a GKE service agent without permission to manage firewalls there cannot create the rules that ingress and load-balancer health checks need. You either grant it a narrow custom role for firewall management in the host project, or have the network team pre-create the rules the cluster will need. Pre-creating is more work but keeps every firewall rule under the security team's review, which is usually the reason Shared VPC was adopted.
Managed services that use private services access, such as Cloud SQL with a private IP, follow the same principle. The allocated range and the peering connection to the service producer live in the host project and are created once by the network team. Service projects then create instances that take addresses from that range. If two teams each try to set up private services access in their own projects, they end up with networks that cannot reach the shared one, which defeats the design.
IP planning and a worked example
Shared VPC concentrates every team's addresses into one plan, so IP planning moves from optional to essential. A workable approach is hierarchical: give each region a large aggregate, carve environment blocks inside it, and allocate team subnets from those blocks. Keep the whole plan clear of on-premises ranges and of ranges used by any partner you might peer with, because overlapping routes cannot be fixed without renumbering.
Worked example. A company has 12 product teams, runs in europe-west1 and us-central1, and has an on-premises network using 10.0.0.0/12. It wants separate production and non-production networks so a misconfigured test firewall cannot expose production. That means two host projects, net-prod and net-nonprod, under a networking folder, with each team's prod project attached to the first and its dev and staging projects attached to the second. A service project can only attach to one host, so this split is decided by project, not by subnet.
| Block | Range | Use |
|---|---|---|
| Prod, europe-west1 | 10.20.0.0/14 | Team node subnets as /22s, plus GKE Pod and Service secondary ranges |
| Prod, us-central1 | 10.24.0.0/14 | Same layout, second region |
| Non-prod, both regions | 10.32.0.0/13 | Dev and staging, same carving rules |
| Private services access | 10.40.0.0/16 | Allocated range for Cloud SQL and similar producers |
A /22 node subnet gives 1,024 addresses, of which Google reserves four, and each regional subnet can be expanded in place later if a team outgrows it. Pod ranges get a separate /16 per cluster carved from the regional block, sized from the node count times the per-node Pod block; a /14 holds four /16s, so this layout suits a few clusters per region, enough here because only the data team runs GKE. The payments team is granted Network User on its two prod subnets only; the data team, which runs GKE, gets its node subnet plus the GKE service agent bindings described above. Firewall rules in net-prod use service-account targets rather than network tags, because a tag can be set by anyone who can edit an instance in a service project, while using a service account requires permission on that account.
The on-premises link, a pair of Cloud Interconnect attachments with Cloud Router, terminates once in net-prod. Every team in every attached project reaches the data centre through it with no extra peering, and Cloud NAT in the host project gives all private VMs controlled egress. That single hybrid edge is often the strongest argument for Shared VPC over per-team networks.
Failure modes
These are the failures that show up after rollout, roughly in order of frequency.
- Instance creation fails with a permission error on the subnet. The caller lacks
compute.subnetworks.useon that subnet. Check which identity really made the call; for MIGs and autoscaling it is the Google APIs service agent, not the engineer. - GKE ingress or load balancers never become healthy. The cluster could not create firewall rules in the host project. Inspect the cluster's events for firewall warnings and either grant the narrow permission or pre-create the rules.
- Pod IP exhaustion. The secondary range was sized for today's node count. Nodes stop scheduling Pods long before the node subnet is full. Size Pod ranges for the maximum node count.
- Project-wide Network User. Granted for convenience during a migration and never narrowed, it lets any service project land resources in any subnet, including sensitive ones. Audit for host-level grants regularly.
- Network tags used as a security boundary. Anyone with instance edit rights in any service project can add a tag and match a host firewall rule. Use service accounts as targets for anything security-relevant.
- Per-network limits. All attached projects share one network, so network-wide limits, such as instance and peering limits per network, are consumed by everyone together. Track them as the number of attached projects grows; per-project quotas such as CPUs stay with each service project.
- Deletion surprises. You cannot delete a host project while it holds the lien, which stays until the project is no longer a host. Decommissioning needs a runbook: move or delete workloads, detach service projects, then disable the host.
Trade-offs and migration
Shared VPC trades team autonomy over the network for central control, and that is the right trade when security review, IP hygiene and hybrid connectivity matter more than letting each team change its own firewall in minutes. It creates a dependency on the network team: if subnet requests take a week, teams will route around the platform. Mitigate this with self-service, for example a Terraform module where a pull request requests a subnet and an approval creates it with the right bindings.
Use peering or Network Connectivity Center instead when networks belong to genuinely separate administrative domains, such as an acquired company or a vendor. Combine Shared VPC with VPC Service Controls when you also need to stop data leaving Google APIs, and with Private Google Access so private VMs on shared subnets can reach Google APIs without external IPs. For the IAM model underneath all of this, see Google Cloud IAM.
Migration is gradual. Attach existing projects as service projects, create new workloads on shared subnets, and move old workloads when they are next rebuilt. Because attaching a project changes nothing about its existing resources, the attachment itself is a low-risk first step.
What to do next
- Decide how many host projects you need; a prod and non-prod pair is the common baseline.
- Write an IP plan with regional aggregates that avoid every on-premises and partner range.
- Grant
roles/compute.xpnAdminto a small group at the folder level, not to individuals at the org. - Enable the host projects and set the two Shared VPC organization policy constraints.
- Grant
roles/compute.networkUserper subnet, including the Google APIs service agent for MIGs. - For GKE, plan Pod and Service secondary ranges and grant the Host Service Agent User role.
- Move security-relevant firewall rules to service-account targets.
- Build a self-service subnet request path so teams do not bypass the platform.
- Audit host-level Network User grants and network-wide limits every quarter.