GCP for DevOps — Cloud NAT

3 minute read

Cloud NAT

Cloud NAT (Network Address Translation) lets VMs without external IP addresses access the internet for outbound connections — such as downloading packages, pulling container images, or calling external APIs — without exposing them to inbound internet traffic.

How Cloud NAT Works

┌──────────────────────────────────────────────────┐
│                    VPC                            │
│                                                  │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│  │ VM-1     │  │ VM-2     │  │ VM-3     │      │
│  │ (no ext  │  │ (no ext  │  │ (no ext  │      │
│  │  IP)     │  │  IP)     │  │  IP)     │      │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘      │
│       │              │              │            │
│       └──────────────┼──────────────┘            │
│                      │                           │
│              ┌───────▼───────┐                   │
│              │  Cloud Router │                   │
│              │  + Cloud NAT  │                   │
│              └───────┬───────┘                   │
│                      │                           │
└──────────────────────┼───────────────────────────┘
                       │
                 ┌─────▼─────┐
                 │ Internet   │
                 │ (outbound  │
                 │  only)     │
                 └───────────┘

Setting Up Cloud NAT

Step 1: Create a Cloud Router

$ gcloud compute routers create nat-router \
    --network=prod-vpc \
    --region=us-central1

Creating router [nat-router]...done.

Step 2: Create Cloud NAT Configuration

# NAT with auto-allocated IPs (simplest)
$ gcloud compute routers nats create prod-nat \
    --router=nat-router \
    --region=us-central1 \
    --nat-all-subnet-ip-ranges \
    --auto-allocate-nat-external-ips

Creating NAT [prod-nat] in router [nat-router]...done.
# NAT with manual (static) IPs
$ gcloud compute addresses create nat-ip-1 nat-ip-2 \
    --region=us-central1

$ gcloud compute routers nats create prod-nat \
    --router=nat-router \
    --region=us-central1 \
    --nat-all-subnet-ip-ranges \
    --nat-external-ip-pool=nat-ip-1,nat-ip-2

# NAT for specific subnets only
$ gcloud compute routers nats create prod-nat \
    --router=nat-router \
    --region=us-central1 \
    --nat-custom-subnet-ip-ranges=prod-app,prod-web \
    --auto-allocate-nat-external-ips

NAT Configuration Options

# View NAT configuration
$ gcloud compute routers nats describe prod-nat \
    --router=nat-router \
    --region=us-central1

enableEndpointIndependentMapping: false
icmpIdleTimeoutSec: 30
minPortsPerVm: 64
name: prod-nat
natIpAllocateOption: AUTO_ONLY
sourceSubnetworkIpRangesToNat: ALL_SUBNETWORKS_ALL_IP_RANGES
tcpEstablishedIdleTimeoutSec: 1200
tcpTransitoryIdleTimeoutSec: 30
udpIdleTimeoutSec: 30
# Update NAT settings — increase ports per VM for high-traffic workloads
$ gcloud compute routers nats update prod-nat \
    --router=nat-router \
    --region=us-central1 \
    --min-ports-per-vm=256 \
    --max-ports-per-vm=4096 \
    --enable-dynamic-port-allocation

# Enable NAT logging
$ gcloud compute routers nats update prod-nat \
    --router=nat-router \
    --region=us-central1 \
    --enable-logging \
    --log-filter=ERRORS_ONLY

NAT Logging

# View NAT logs
$ gcloud logging read 'resource.type="nat_gateway"' \
    --project=my-project --limit=10 \
    --format="table(timestamp,jsonPayload.connection.src_ip,jsonPayload.connection.dest_ip,jsonPayload.allocation_status)"

Static NAT IP for Allowlisting

When external services need to allowlist your outbound IPs:

# Reserve static IPs
$ gcloud compute addresses create nat-egress-ip \
    --region=us-central1

$ gcloud compute addresses describe nat-egress-ip --region=us-central1 \
    --format="get(address)"

34.72.100.50

# Use the static IP in NAT
$ gcloud compute routers nats update prod-nat \
    --router=nat-router \
    --region=us-central1 \
    --nat-external-ip-pool=nat-egress-ip

# Share this IP with the external service for allowlisting

Cloud NAT vs External IPs

Feature Cloud NAT External IP on VM
Outbound internet ✅ Yes ✅ Yes
Inbound from internet ❌ No ✅ Yes (if firewall allows)
Security ✅ Better — no inbound exposure ⚠️ VM exposed to internet
Cost NAT gateway charges Only IP cost
Static IP for allowlisting ✅ NAT IP pool ✅ Reserved static IP
Managed ✅ Fully managed You manage firewall

Common Mistakes

Mistake Why It’s Wrong Fix
Not creating Cloud NAT Private VMs can’t reach the internet Always create NAT for private subnets
Default minPortsPerVm=64 Not enough for high-traffic VMs Increase to 256+ for production
Not enabling logging Can’t debug connectivity Enable with ERRORS_ONLY filter
Auto IPs for allowlisting IPs can change Use manual static IPs
One NAT for all subnets No isolation Create separate NATs per environment

Quick Reference

Command Description
gcloud compute routers create NAME --network=VPC --region=R Create Cloud Router
gcloud compute routers nats create NAME --router=R --region=R Create Cloud NAT
--nat-all-subnet-ip-ranges NAT all subnets
--nat-custom-subnet-ip-ranges=S1,S2 NAT specific subnets
--auto-allocate-nat-external-ips Auto-assign NAT IPs
--nat-external-ip-pool=IP1,IP2 Use static NAT IPs
gcloud compute routers nats describe NAME View NAT config

Next → Cloud Load Balancing

Updated: