Our first real production scan of Fintropy’s own GCP project turned up a resource we didn’t know we were paying for: an idle Cloud NAT gateway, billing $32.12 a month to move exactly 0 bytes over seven days. Nobody provisioned it on purpose. Terraform boilerplate did - and a serverless stack made sure it would never carry a packet.

The convenience that costs you

It started, as these things do, with a “standard VPC” module. You call it, you get a sensible, batteries-included network: subnets, firewall rules, a router, and - helpfully - a Cloud NAT so any VMs you spin up can reach the internet without public IPs.

That default is great advice for the world the module was written for: VMs in a private subnet that need egress. It’s the wrong default for ours.

Our stack has no VMs. It runs on Cloud Run, egress restricted to private ranges only, talking to a private Cloud SQL instance over a Serverless VPC connector. Every byte that leaves our services either stays inside the VPC or goes out through Google’s own managed paths. Public egress through the VPC router? Zero. The NAT sat in the path of traffic that never came.

The giveaway was sitting in the module itself - a comment on the connector:

# connector used by Cloud Run to reach private SQL

Private SQL. Private ranges. A NAT gateway whose entire job is public egress, wired into a stack that has none. It was decorative from the moment terraform apply finished - commit e5bf83c9.

Idle is not the same as cheap

Here’s the part that makes idle cloud resources so easy to miss: they don’t bill for what they do. They bill for existing.

A Cloud NAT gateway runs a per-hour charge whether it forwards a terabyte or nothing at all:

$0.044/hr × 730 hrs/mo = $32.12/mo

No traffic, no per-GB data processing charge - but the gateway meter never stops. Thirty-two dollars, every month, for a resource whose only measurable output was a line item. Multiply that reflex across every “standard VPC” a team stamps out and the idle tax stops being a rounding error.

Dogfooding caught it

The satisfying part: we didn’t find this by manually spelunking the console. Fintropy found it.

Our scanner runs keyless, hosted on Azure, reading the GCP project through granted read-only roles. On its first pass it flagged the NAT as idle. We didn’t take the flag on faith - we verified it against live Cloud Monitoring:

  • Sent bytes: 0 over 7 days
  • Received bytes: 0 over 7 days
  • Open connections: 0
  • VMs in the project: 0
  • GKE nodes: 0

Nothing upstream of the gateway could have generated traffic, and Monitoring confirmed none did. So we deleted it. The scan paid for a good chunk of its own existence on the first resource it touched - which is exactly the pitch we make to customers, run against our own bill.

The moral

  • IaC defaults optimize for the general case, not yours. A module that bundles a NAT “just in case you have VMs” is doing you a favor right up until your architecture doesn’t have VMs. Read what the convenience actually provisions.
  • Idle resources bill for uptime, not usage. A 0-byte meter reading is not a $0 invoice line. The scariest waste is the resource that’s working perfectly - at doing nothing.
  • Trust, then verify against telemetry. A flag is a hypothesis. Zero sent, zero received, zero connections, zero VMs - that’s the proof that lets you hit delete without a second thought.

A $32 gateway to nowhere, found by the thing we built to find it. Order from chaos, starting with our own.


Amit Jethva is the CTO and co-founder of Nuvika Technologies Pvt Ltd, makers of Fintropy, a multi-cloud FinOps platform. Learn more at nuvikatech.com.