Overview and What You Will Learn
In this lab, you will create a VM with no external IP address at all, confirm it initially has no internet access, then configure Cloud NAT and observe that same VM successfully reaching the internet outbound - while remaining completely unreachable from the internet inbound the entire time.
Why This Matters in Production
A team needs their private database VMs to download OS security patches periodically, but assigning each one a public IP just to enable outbound internet access would also make them directly reachable from the internet inbound - a meaningful, unnecessary security exposure for infrastructure that should never accept incoming connections from outside the VPC at all. Cloud NAT solves exactly this: outbound internet access without any inbound exposure.
Core Principles
Cloud NAT provides outbound-only internet connectivity for resources with no external IP address, the GCP equivalent of an AWS NAT Gateway or Azure NAT Gateway - private resources can initiate connections out to the internet, but nothing on the internet can initiate a connection in.
+------------------------------------------+| VM with no external IP address |+------------------------------------------+ | | outbound request only v+------------------------------------------+| Cloud NAT || Translates the private IP to a shared or || dedicated external IP for outbound traffic |+------------------------------------------+ | v+------------------------------------------+| Internet (response traffic returns through || the same NAT, but no new inbound connection || can be initiated from here) |+------------------------------------------+Cloud NAT requires a Cloud Router in the same region, since NAT configuration is actually attached to the router resource rather than existing as a fully standalone service.
Detailed Step-by-Step Practical Lab
- Create a project, a VPC, and a subnet:
gcloud projects create gcp-nat-lab-2026 --name="Cloud NAT Lab"gcloud config set project gcp-nat-lab-2026gcloud services enable compute.googleapis.com gcloud compute networks create vpc-nat-lab --subnet-mode=customgcloud compute networks subnets create subnet-nat-lab \ --network=vpc-nat-lab \ --region=asia-south1 \ --range=10.0.1.0/24- Create a VM with no external IP address:
gcloud compute instances create vm-private \ --zone=asia-south1-a \ --machine-type=e2-small \ --image-family=debian-12 \ --image-project=debian-cloud \ --network=vpc-nat-lab \ --subnet=subnet-nat-lab \ --no-address- SSH into the VM (via a bastion or Identity-Aware Proxy, since it has no external IP for direct SSH) and confirm it initially has no internet access:
gcloud compute ssh vm-private --zone=asia-south1-a --tunnel-through-iap curl -m 5 https://www.google.com## Expected: this times out or fails, since there is no route to the## internet yet without Cloud NAT configured- Create a Cloud Router, required as the foundation Cloud NAT attaches to:
gcloud compute routers create router-nat-lab \ --network=vpc-nat-lab \ --region=asia-south1- Create the Cloud NAT configuration attached to that router:
gcloud compute routers nats create nat-config-lab \ --router=router-nat-lab \ --region=asia-south1 \ --auto-allocate-nat-external-ips \ --nat-all-subnet-ip-ranges- Retry the same connectivity test from inside the VM, now succeeding:
# Back inside the VM:curl -m 5 https://www.google.com## Expected: this now succeeds, since Cloud NAT provides outbound## connectivity even though the VM still has no external IP of its own- Confirm the VM remains unreachable from the internet inbound, despite now having outbound access - Cloud NAT is strictly one-directional:
# From outside the VPC entirely, attempting to reach the VM's# (nonexistent) external IP would simply fail, since it never has onegcloud compute instances describe vm-private \ --zone=asia-south1-a \ --format="value(networkInterfaces[0].accessConfigs)"## Expected: empty output, confirming no external IP or access config exists- Clean up:
gcloud compute instances delete vm-private --zone=asia-south1-a --quietgcloud compute routers nats delete nat-config-lab --router=router-nat-lab --region=asia-south1 --quietgcloud compute routers delete router-nat-lab --region=asia-south1 --quietgcloud compute networks subnets delete subnet-nat-lab --region=asia-south1 --quietgcloud compute networks delete vpc-nat-lab --quietgcloud projects delete gcp-nat-lab-2026 --quietProduction Best Practices & Common Pitfalls
Common MistakeAssigning a public IP to a private-subnet resource purely to give it outbound internet access for patching or downloading dependencies, when Cloud NAT provides the same outbound capability with zero inbound exposure. A public IP granted "just for updates" is a common, avoidable source of unintended internet-facing exposure.
Tip
--nat-all-subnet-ip-rangesconfigures NAT for every subnet in the region automatically - for more granular control, specific subnets (or even specific IP ranges within a subnet) can be selected individually instead, useful when only certain private resources should have outbound internet access.
- Cloud NAT requires a Cloud Router in the same region, even though the router itself may not be actively used for dynamic route exchange in a simple setup - it's the resource NAT configuration is technically attached to.
- Cloud NAT does not provide any inbound connectivity capability whatsoever. A resource behind Cloud NAT with no external IP remains completely unreachable from the internet inbound, regardless of NAT configuration - this asymmetry is the entire security value Cloud NAT provides.
Quick Reference & Troubleshooting Commands
| Command | Description |
|---|---|
gcloud compute routers create |
Create the Cloud Router NAT attaches to |
gcloud compute routers nats create |
Configure Cloud NAT on a router |
gcloud compute instances create --no-address |
Create a VM with no external IP |
gcloud compute ssh --tunnel-through-iap |
SSH into a private VM via Identity-Aware Proxy |