# Idle Transit Gateway -- No Traffic

> A Transit Gateway bills roughly $36 a month for every attachment even when nothing crosses it. ZopNight reads BytesIn and BytesOut over 30 days and flags the gateway only when both directions sit at exact zero; one direction alone is never enough, and missing data means no finding.

Source: https://zop.dev/integrations/aws/recommendations/idle-transit-gateway-no-traffic
Updated: 2026-08-19

---

## Attachments are the bill, not the gateway

A Transit Gateway has no base charge of its own. AWS bills per attachment-hour, roughly $36
a month for each VPC, VPN or Direct Connect attachment, plus [a per-GB processing fee on the
traffic that crosses](https://aws.amazon.com/transit-gateway/pricing/). So an abandoned TGW is not free: every attachment left in place keeps
its own meter running, while the data-processing line reads zero precisely because nothing
moves anymore.

The same pricing shape explains an edge case. A gateway with no attachments at all prices out
to $0, and the rule drops it rather than recommend deleting something that already costs
nothing. Whatever survives to become a finding is real, recoverable attachment spend.

## Zero in both directions, or nothing

Two of [the CloudWatch counters under `AWS/TransitGateway`](https://docs.aws.amazon.com/vpc/latest/tgw/transit-gateway-cloudwatch-metrics.html) carry the evidence: `BytesIn` and
`BytesOut`, each over a 30-day window. The gateway is idle only when the average **and** the
maximum of both series are exactly 0. A gateway that only egresses, or only ingresses, is
still doing a job and is never flagged. An earlier version of this check keyed on an
`attachments=0` tag that no pipeline ever populated, leaving it permanently silent, and the
metric-based test replaced it.

## Pulling BytesIn and BytesOut yourself

```bash
aws cloudwatch get-metric-statistics \
  --namespace AWS/TransitGateway --metric-name BytesIn \
  --dimensions Name=TransitGateway,Value=tgw-0123456789abcdef0 \
  --start-time "$(date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)" \
  --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  --period 86400 --statistics Sum Maximum
```

Run it a second time with `--metric-name BytesOut`. Both have to come back flat for the
finding to be valid.

## Why some idle gateways still go unreported

The rule abstains whenever either byte series is absent or has less than a week of recorded
coverage, whenever any sample is non-zero, and whenever no positive monthly price is on
record. Beyond those data gates, emission currently sits behind a feature flag: findings stay
held back until the aggregator fix this rule depends on is confirmed rolled out to stage.
Severity is deliberately low: this is housekeeping, not a cost emergency.

## Detaching before deleting

Removal is a guided workflow, not a one-shot delete. Confirm no VPC, VPN or Direct Connect
gateway still terminates on the TGW, remove the remaining route-table associations, then
delete the gateway itself. Any CloudFormation stack that references it needs updating too,
or the next stack operation will fail on the resource you just
removed, or quietly recreate it.
