Site-to-Site VPN connections billing hourly with no tunnel ever up
What does ZopNight detect here?
AWS Site-to-Site VPN bills a fixed connection-hour rate from the moment a connection exists, tunnels up or not. ZopNight requires 3 fully-covered CloudWatch axes over 30 days (TunnelState peaking at zero plus combined tunnel bytes under 1 MiB) before calling a connection idle and claiming its full measured cost.
Signal and threshold
| Field | Value |
|---|---|
| Rule IDs | RC-162 |
| Category | idle |
| Severity | medium |
| Metric | none — pure configuration read |
| Source | vpc_unused_vpn.go |
Where it applies
Dead on both ends, billed in the middle
A Site-to-Site VPN whose customer gateway was decommissioned (the office closed, the appliance replaced, the migration finished) keeps billing its connection-hour fee indefinitely. Nothing on the AWS side notices that the far end no longer exists. These connections are common leftovers of network migrations, and the fee runs until someone deletes the connection object itself.
Three axes, all mandatory
Idleness here is corroborated across the AWS/VPN metrics a connection publishes, and any one of them can veto. TunnelState is the health gauge: the rule requires its window peak to be zero, meaning no tunnel was up at any moment in 30 days. One minute of connectivity anywhere in the window disqualifies. TunnelDataIn and TunnelDataOut are the traffic axes: their combined peak must sit at or below a 1 MiB floor, tolerating the keep-alive and dead-peer-detection chatter an up-but-unused tunnel emits while any real transfer disqualifies. Each axis must be present with a full 30 days of coverage; a missing or thin series abstains, because a connection you failed to measure is not a connection proven dead.
The cost side has the same discipline
The saving is the connection’s measured monthly fixed fee from the pricing pipeline, never a published-rate guess, and an unpriced connection abstains. Only connections in the available state are considered at all; anything mid-provisioning or deleting is in a different lifecycle and out of scope.
Reproduce the tunnel check
aws cloudwatch get-metric-statistics \ --namespace AWS/VPN --metric-name TunnelState \ --dimensions Name=VpnId,Value=vpn-0123456789abcdef0 \ --start-time "$(date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)" \ --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ --period 3600 --statistics MaximumEvery datapoint at 0 is the no-tunnel-ever-up condition; repeat with TunnelDataIn and TunnelDataOut for the traffic axes.
Deleting the whole chain
The connection is only part of the bill of materials. After deleting it, check whether the virtual private gateway it attached to serves any other connection (an orphaned VGW is the next cleanup), and remove static routes referencing the VPN from your route tables. If the link is meant to come back someday, the honest comparison is redeploying the connection when needed versus paying the hourly fee to keep a dead one warm.