Skip to content
AssociateOpenAWS VPCNAT Gateway

Lab 01: The Misplaced NAT Gateway

A batch job in a private subnet cannot reach a third-party API. The route table looks correct, the security groups are wide open, and the NAT Gateway reports Available. Find out why every outbound connection still hangs.

Debugging time
~20 min
Reading time
12 min
Reported by
Payments Platform
Tier
Associate
INC-1043SEV-3InvestigatingOpened Today 09:14 UTC

Outbound API calls from the private subnet time out after deploying the new VPC

Reported by Payments Platform

Environment
staging
Region
us-east-1
VPC
10.0.0.0/16
Affected subnet
10.0.11.0/24 (private-a)

We cut over the settlement batch job to the new staging VPC last night. The job starts, then every call to our payment processor's API hangs and eventually times out. No response, no TLS error, nothing in the logs except the client-side timeout.

Things we already checked:

  • The NAT Gateway shows Available in the console with an Elastic IP attached.
  • The private subnet's route table has 0.0.0.0/0 pointed at the NAT Gateway.
  • The instance security group allows all outbound traffic.
  • DNS resolves fine from the instance.

Networking says the config "looks right." We are blocked on the settlement run.

What you are working with

A single-AZ VPC with the usual two-tier layout. One public subnet, one private subnet, an internet gateway, and a NAT Gateway that is supposed to give the private tier outbound access.

Starting state — which subnet holds which resource
VPC 10.0.0.0/16 Attached: Internet gateway, S3 gateway endpoint. Availability Zone A contains subnet-public 10.0.0.0/24, default route 0.0.0.0/0, holding no resources; subnet-private 10.0.11.0/24, default route 0.0.0.0/0, holding NAT Gateway, Batch instance, SSM interface endpoints.api.processor.example52.94.236.248:443VPC 10.0.0.0/16Availability Zone Asubnet-publicpublic10.0.0.0/24no resourcessubnet-privateprivate10.0.11.0/240.0.0.0/0NAT Gatewaystate available, EIP attachedBatch instancesg-app allows all egressSSM interface endpointsssm, ssmmessages, ec2messages0.0.0.0/0Internet gatewayattached to the VPCS3 gateway endpointprefix-list route inrtb-private

Everything is in the state the team reported: the NAT Gateway is available with an Elastic IP attached, and the private route table points at it. The diagram is accurate — the fault is in it.

10.0.11.x:49820 → 52.94.236.248:443 (TCP SYN, outbound HTTPS)
  1. EC2

    Batch instance

    i-… in subnet-private-a (10.0.11.0/24)

  2. FILTER

    Security group egress

    sg-app — allow all outbound (0.0.0.0/0)

  3. FILTER

    Network ACL

    default NACL — allow all inbound and outbound

  4. RTB

    Private route table

    rtb-private — 0.0.0.0/0 → nat-…

  5. GW

    NAT Gateway

    nat-… — state: available, EIP attached

  6. DEST

    api.processor.example

    52.94.236.248:443

Every hop the team checked passes. The packet reaches the NAT Gateway. What happens after that is the lab.

Scope and constraints

  • In scope: VPC routing, subnet placement, NAT Gateway configuration.
  • Out of scope: the third-party API itself, TLS, application code, DNS. The endpoint is reachable from anywhere with working egress.
  • The instance has no public IP and must stay that way. Assigning one is not the fix.
  • You have Session Manager access to the instance. It works because the lab provisions interface endpoints for SSM inside the VPC, so your shell does not depend on the broken egress path.
  • tcpdump and dig are already installed. Neither ships in the AL2023 base image, and an instance with no egress cannot fetch them — so the lab installs both at launch over an S3 gateway endpoint, which is where AL2023 serves its package repositories from. That endpoint carries S3 traffic only, so the egress failure you are here to diagnose is untouched.

Deploy the broken state

main.tf5 lines

Download the file below into an empty directory, then apply it. The apply succeeds — nothing here is invalid, which is exactly why this class of bug survives code review.

terraform init
terraform apply

# Grab the session command from the outputs
terraform output -raw start_session_command

Full source: main.tf — one file, no modules, no remote state. It provisions a VPC, both subnets, an internet gateway, a NAT Gateway with an Elastic IP, both route tables, SSM interface endpoints, and a t3.micro running Amazon Linux 2023.

Apply takes about three minutes, most of it waiting on the NAT Gateway and the endpoints.