Skip to content
ProfessionalProNAT GatewayTransit GatewayAWS VPC

Lab 20: The Egress VPC That Routed Around Its Own NAT Gateway

Centralised egress, built to the reference architecture. The NAT Gateway is available, both attachments are available, every route is active, and the egress VPC itself reaches the internet fine. The spokes get nothing, and no log anywhere records a drop.

Debugging time
~30 min
Reading time
15 min
Reported by
Platform Engineering
Tier
Professional
INC-1791SEV-2InvestigatingOpened 2026-09-28 16:05 UTC

All spoke VPCs lost internet access after cutover to the central egress VPC; egress VPC itself is fine

Reported by Platform Engineering

We cut the first spoke over to the new central egress VPC this afternoon. Its default route now goes to the transit gateway instead of its own NAT Gateway, which we deleted as planned. Since then, outbound from the spoke times out. Package installs, API calls, everything.

I have checked the obvious things. Both transit gateway attachments are available. The spoke's route table sends 0.0.0.0/0 to the transit gateway. The transit gateway route table sends 0.0.0.0/0 to the egress attachment. The egress VPC's NAT Gateway is available with an Elastic IP. I can log into a host in the egress VPC and curl the internet without any problem, so the egress VPC can clearly get out.

I enabled flow logs on the egress side to see where it is being blocked and there is not a single REJECT. Every record is ACCEPT. So nothing is blocking it, and it still does not work.

What you are working with

One Transit Gateway, two VPCs, one Availability Zone.

Starting state — a spoke behind a central egress VPC
VPC spoke 10.200.0.0/16 AZ a contains workload 10.200.1.0/24, default route 0.0.0.0/0 → tgw, holding Workload. VPC egress 10.201.0.0/16 Attached: Internet gateway. AZ a contains tgw attachment 10.201.0.0/24, default route 0.0.0.0/0 → ?, holding no resources; public 10.201.1.0/24, default route 0.0.0.0/0 → igw, holding NAT Gateway, Control host.the internettransitgatewayspoke default →egressVPC spoke 10.200.0.0/16AZ aworkloadprivate10.200.1.0/240.0.0.0/0 → tgwWorkload10.200.1.50VPC egress 10.201.0.0/16AZ atgw attachmentprivate10.201.0.0/240.0.0.0/0 → ?no resourcespublicpublic10.201.1.0/240.0.0.0/0 → igwNATGatewayavailableControlhost10.201.1.50,public IPInternet gateway

The spoke has no internet gateway and no NAT Gateway of its own any more. Its default route reaches the transit gateway, which delivers it to the egress VPC's attachment subnet. What that subnet's route table does with it is the one thing this diagram does not state, because it is the thing you are going to read. The control host sits in the public subnet with its own public address, which is why it can reach the internet and why that proves less than it seems to.

ResourceConfiguration
WorkloadSpoke VPC, 10.200.1.50, private subnet, default route → transit gateway
Control hostEgress VPC public subnet, 10.201.1.50, has a public IPv4 address
Transit gatewaySpokes route table: 0.0.0.0/0 → egress attachment. Egress table: spoke prefix propagated
NAT GatewayEgress VPC public subnet, available, Elastic IP attached
Public subnet route table0.0.0.0/0 → internet gateway; 10.200.0.0/16 → transit gateway
Attachment subnet route table0.0.0.0/0 → see Reproduction
Flow logsEnabled on the egress VPC's attachment subnet, all traffic

Every object is available. Every route is active. There is no security group or network ACL in the path that denies anything.

spoke workload → 1.1.1.1:443
  1. EC2

    Workload

    10.200.1.50 → 1.1.1.1:443

  2. GW

    Transit gateway

    spokes route table: 0.0.0.0/0 → egress attachment

  3. SUBNET

    Egress attachment subnet

    flow log: ACCEPT, src 10.200.1.50

  4. RTB

    Attachment subnet route table

    0.0.0.0/0 → the target it was given

    Dropped — The packet is handed to a target that is valid for the route table and accepts it. The reply never comes. Nothing records why.

  5. DEST

    1.1.1.1

    never sees a SYN

The packet travels three hops correctly and is accepted into the egress VPC. The fourth hop is where it goes somewhere and does not come back, and the absence of a REJECT is the clue rather than the reassurance the reporter took it for.

Scope and constraints

  • In scope: why the spoke's traffic reaches the egress VPC and never reaches the internet, when the egress VPC itself can.
  • Out of scope: the transit gateway configuration, the spoke's route table, the NAT Gateway's health, security groups, and network ACLs. All correct, and the reproduction confirms each.
  • The reporter is right that nothing is rejecting the traffic. Hold on to that: it is a real observation and it narrows the search considerably.
  • The reporter's test from the egress VPC is also genuinely true and genuinely misleading. Work out why before you trust it.
  • The fix is a one-line change to one route.

Deploy the broken state

cd lab-20-egress-vpc-igw-drop
terraform init
terraform apply

The transit gateway takes a few minutes and the attachments a few more — budget about eight minutes. The spoke host starts a probe on boot that attempts one connection a minute to 1.1.1.1:443, so the egress VPC's flow logs will have evidence waiting by the time you look.