GCP has two network tiers: Premium ($0.12/GB) and Standard ($0.085/GB). Premium routes traffic over Google's private backbone. Standard uses the public internet. The catch? Premium is the default for every resource you create. Your dev VMs, staging APIs, internal tools - all paying premium egress rates for traffic that doesn't need Google's backbone. Switch non-critical workloads to Standard Tier and save 30-45% on egress instantly.
Most teams don't even know GCP has network tiers. They create resources, traffic flows, and the egress line item on their bill grows silently. For a workload doing 5 TB of egress per month in North America, the difference between Premium and Standard is roughly $225/month - just from one config change.
And that's before we add Cloud CDN, which can cut egress costs by another 60-80% for cacheable content.
Let's fix both with Terraform.
š The Egress Pricing Cheat Sheet
Premium Tier (default):
| Monthly Volume | North America | Europe | Asia |
|---|---|---|---|
| 0-1 TB | $0.12/GB | $0.12/GB | $0.12/GB |
| 1-10 TB | $0.11/GB | $0.11/GB | $0.11/GB |
| 10+ TB | $0.08/GB | $0.08/GB | $0.08/GB |
Standard Tier (opt-in):
| Monthly Volume | North America | Europe | Asia |
|---|---|---|---|
| 0-1 TB | $0.085/GB | $0.085/GB | $0.11/GB |
| 1-10 TB | $0.065/GB | $0.065/GB | $0.095/GB |
| 10+ TB | $0.045/GB | $0.045/GB | $0.065/GB |
Side-by-side for 5 TB/month (North America):
| Tier | Cost | Savings |
|---|---|---|
| Premium | ~$570 | baseline |
| Standard | ~$345 | $225/month (39% saved) |
| Standard + Cloud CDN (70% cache hit) | ~$135 | $435/month (76% saved) |
ā ļø Important tradeoff: Standard Tier routes traffic via the public internet. Latency is 50-100ms higher for users far from your GCP region. This matters for real-time apps serving global users. It does NOT matter for dev/staging, internal tools, B2B APIs, or batch workloads.
š§ Step 1: Set Standard Tier as Project Default for Non-Prod
Set the default network tier at the project level so every new resource in that project uses Standard automatically:
# Set Standard Tier as the default for non-prod projects
resource "google_compute_project_default_network_tier" "default" {
project = var.project_id
network_tier = var.environment == "prod" ? "PREMIUM" : "STANDARD"
}
This one resource changes the default for every new external IP, forwarding rule, and instance in the project. Existing resources are not affected - only new ones.
ā ļø Gotcha: Standard Tier does NOT support global load balancing, Cloud CDN, or anycast IPs. If you need any of these features, use Premium Tier for those specific resources. You can mix tiers within the same project.
š„ļø Step 2: Per-Resource Network Tier Control
For finer control, set the network tier on individual resources:
# Production: Premium Tier for user-facing APIs
resource "google_compute_instance" "prod_api" {
name = "prod-api"
machine_type = "e2-custom-4-8192"
zone = var.zone
network_interface {
network = var.network_id
subnetwork = var.subnet_id
access_config {
network_tier = "PREMIUM" # Global backbone for end users
}
}
labels = merge(local.common_labels, {
network-tier = "premium"
})
}
# Dev/Staging: Standard Tier saves 30-45%
resource "google_compute_instance" "staging_api" {
name = "staging-api"
machine_type = "e2-custom-2-4096"
zone = var.zone
network_interface {
network = var.network_id
subnetwork = var.subnet_id
access_config {
network_tier = "STANDARD" # Public internet, 30-45% cheaper
}
}
labels = merge(local.common_labels, {
network-tier = "standard"
})
}
Decision matrix for which tier to use:
| Workload | Tier | Why |
|---|---|---|
| User-facing production APIs | Premium | Latency matters for UX |
| Dev/staging environments | Standard | Nobody notices 50ms more |
| Internal tools and dashboards | Standard | Used by employees only |
| B2B APIs with known clients | Standard | Clients have stable connections |
| Batch data processing | Standard | Latency is irrelevant |
| Global consumer apps | Premium | Users worldwide need low latency |
| CI/CD runners | Standard | Build artifacts don't need backbone |
š Step 3: Cloud CDN for Static Assets
Cloud CDN caches content at Google's edge locations worldwide. Cache hits are served from the nearest edge, dramatically reducing origin egress:
# Backend bucket pointing to Cloud Storage
resource "google_compute_backend_bucket" "cdn_assets" {
name = "cdn-static-assets"
bucket_name = google_storage_bucket.static_assets.name
enable_cdn = true
cdn_policy {
cache_mode = "CACHE_ALL_STATIC"
default_ttl = 3600 # 1 hour default
max_ttl = 86400 # 24 hours max
client_ttl = 3600
serve_while_stale = 86400 # Serve stale for 24hr if origin is down
cache_key_policy {
include_http_headers = []
}
}
}
# GCS bucket for static content
resource "google_storage_bucket" "static_assets" {
name = "${var.project_id}-static-assets"
location = "US"
uniform_bucket_level_access = true
labels = local.common_labels
}
# Make objects publicly readable for CDN
resource "google_storage_bucket_iam_member" "public_read" {
bucket = google_storage_bucket.static_assets.name
role = "roles/storage.objectViewer"
member = "allUsers"
}
# URL map
resource "google_compute_url_map" "cdn" {
name = "cdn-url-map"
default_service = google_compute_backend_bucket.cdn_assets.id
}
# HTTPS proxy
resource "google_compute_target_https_proxy" "cdn" {
name = "cdn-https-proxy"
url_map = google_compute_url_map.cdn.id
ssl_certificates = [var.ssl_certificate_id]
}
# Global forwarding rule (requires Premium Tier)
resource "google_compute_global_forwarding_rule" "cdn" {
name = "cdn-forwarding-rule"
target = google_compute_target_https_proxy.cdn.id
port_range = "443"
ip_protocol = "TCP"
}
Cost comparison for serving 2 TB/month of static assets (North America):
| Method | Cost |
|---|---|
| Direct from GCS (Premium egress) | ~$240/month |
| Direct from GCS (Standard egress) | ~$170/month |
| Cloud CDN (70% cache hit rate) | ~$85/month |
| Cloud CDN (90% cache hit rate) | ~$50/month |
That's $190/month saved with Cloud CDN at 70% cache hit rate vs direct GCS. Cache hit rates for static assets (images, CSS, JS) routinely hit 85-95%.
ā ļø Cloud CDN requires Premium Tier for the forwarding rule. But the CDN cache egress rate ($0.08/GB for North America) is cheaper than both Premium and Standard direct egress. So you pay Premium for the infrastructure but get lower per-GB rates from the cache.
š Step 4: Private Google Access (Free Savings)
VMs without external IPs that need to reach Google APIs (Cloud Storage, BigQuery, etc.) normally route through Cloud NAT, which charges $0.045/GB processing. Private Google Access lets them reach Google APIs directly at zero cost:
resource "google_compute_subnetwork" "private" {
name = "private-subnet"
ip_cidr_range = "10.0.1.0/24"
region = var.region
network = var.network_id
# This one flag saves $0.045/GB on all Google API traffic
private_ip_google_access = true # š Free, enable everywhere
}
This costs nothing to enable and immediately eliminates NAT processing charges for Google API traffic. Every subnet should have this turned on.
Savings for 500 GB/month of Google API traffic from private VMs:
| Route | Cost |
|---|---|
| Via Cloud NAT | $22.50/month ($0.045/GB) |
| Via Private Google Access | $0/month |
š¢ Step 5: Cross-Region Traffic Audit
Same-region traffic between GCP services is usually free. Cross-region traffic costs $0.01/GB. This adds up when services are accidentally deployed in different regions:
# Ensure all resources are in the same region
variable "region" {
type = string
default = "us-central1"
description = "Primary region - keep everything here to avoid cross-region egress"
}
# Validate that storage and compute are co-located
resource "google_storage_bucket" "data" {
name = "${var.project_id}-data"
location = var.region # Same region as compute!
labels = local.common_labels
}
resource "google_compute_instance" "worker" {
name = "data-worker"
machine_type = "e2-custom-4-8192"
zone = "${var.region}-a" # Same region as storage!
# ...
}
ā ļø Common mistake: Creating a Cloud Storage bucket with
location = "US"(multi-region) and compute inus-central1(single region). Traffic between them may incur cross-region charges. For cost optimization, use the same single region for both.
š” Quick Reference: What to Do First
| Action | Effort | Savings |
|---|---|---|
| Set Standard Tier default for non-prod projects | 5 min | 30-45% on non-prod egress |
| Enable Private Google Access on all subnets | 5 min | $0.045/GB on Google API traffic |
| Switch dev/staging VMs to Standard Tier | 10 min | 30-45% per VM egress |
| Add Cloud CDN for static assets | 20 min | 60-80% on cacheable content |
| Audit cross-region resource placement | 15 min | Eliminate $0.01/GB cross-region fees |
| Remove external IPs from internal-only VMs | 10 min | Eliminates accidental egress |
Start with Private Google Access. It's one line in Terraform, costs $0, and starts saving immediately. šÆ
š TL;DR
Premium Tier (default) = $0.12/GB, Google backbone, low latency
Standard Tier (opt-in) = $0.085/GB, public internet, 30-45% cheaper
Project default tier = one resource changes all new instances
Per-resource override = mix Premium (prod) and Standard (dev)
Cloud CDN = cache at edge, 60-80% less origin egress
Cloud CDN requires Premium = but cache egress is cheaper than both tiers
Private Google Access = free, eliminates NAT charges for API traffic
Same-region traffic = free between most GCP services
Cross-region traffic = $0.01/GB, avoid by co-locating resources
Internal IP vs external IP = same-zone traffic is free only with internal
Bottom line: GCP defaults everything to Premium Tier, and most teams never change it. Switching non-critical workloads to Standard saves 30-45% on egress. Adding Cloud CDN saves another 60-80% on cacheable content. Enabling Private Google Access is free and immediate. Combined, these three moves can cut your networking bill by 70%+. š
Go check your non-prod projects right now. If they're still on Premium Tier, you're paying a 40% markup for network performance that literally nobody notices on a staging server. One Terraform resource fixes it. š
Found this helpful? Follow for more GCP cost optimization with Terraform! š¬
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.