Core Implementation: Route Tables and NAT Gateways

Route Table Architecture

The routing layer is where the isolation you designed in subnets becomes operational. At this point, all six subnets are associated with the main route table, which only has a local route. Nothing can reach the internet and nothing can receive traffic from the internet. You will now build three distinct routing tiers.

You need four route tables:

  • rtb-public — routes 0.0.0.0/0 to the IGW; associated with both public subnets
  • rtb-private-app-az1 — routes 0.0.0.0/0 to NAT-AZ1; associated with private-app-az1
  • rtb-private-app-az2 — routes 0.0.0.0/0 to NAT-AZ2; associated with private-app-az2
  • rtb-private-db — no default route; associated with both DB subnets

The DB subnets intentionally have no route to the internet. Database instances should never initiate or receive internet connections. If your database needs to pull updates, the recommended pattern is to use a VPC endpoint for AWS services or route through a proxy in the application tier.

Creating NAT Gateways

Navigate to VPC > NAT Gateways > Create NAT Gateway. Create two NAT Gateways, one per AZ.

For each:

  • Subnet: select the corresponding public subnet (public-az1 for the first, public-az2 for the second)
  • Connectivity type: Public
  • Elastic IP: click “Allocate Elastic IP” to create a new one

Name them nat-az1 and nat-az2.

NAT Gateways take 1–2 minutes to reach “Available” state. Wait before proceeding to the route table step — associating a route with a pending NAT Gateway causes intermittent failures.

What you should see: Both NAT Gateways in “Available” state, each with an Elastic IP and placed in the correct public subnet.

Why public subnets for NAT Gateways? A NAT Gateway must itself have a path to the internet to forward traffic on behalf of private instances. Placing it in a public subnet (which has the IGW route) satisfies this requirement. Placing a NAT Gateway in a private subnet is a configuration error that results in an “Available” gateway that drops all outbound traffic silently.

Creating and Associating Route Tables

Navigate to VPC > Route Tables > Create route table.

Public route table (rtb-public):

  • After creation, go to Routes > Edit routes > Add route
  • Destination: 0.0.0.0/0, Target: select Internet Gateway → lab-igw
  • Go to Subnet associations > Edit subnet associations
  • Associate public-az1 and public-az2

Private app route tables:

  • Create rtb-private-app-az1, add route 0.0.0.0/0nat-az1, associate private-app-az1
  • Create rtb-private-app-az2, add route 0.0.0.0/0nat-az2, associate private-app-az2

Database route table (rtb-private-db):

  • Create rtb-private-db, add no additional routes beyond the local route
  • Associate private-db-az1 and private-db-az2

What you should see: Each route table shows the correct number of associated subnets. The public route table shows two routes (local + IGW). The app route tables each show two routes (local + NAT). The DB route table shows one route (local only).

Validating connectivity intent: At this point, launch a test EC2 instance (Amazon Linux 2, t3.micro) in private-app-az1 with no public IP. Use EC2 Instance Connect or SSM Session Manager to access it. Run:

curl -s --max-time 5 https://example.com

You should receive an HTTP response. If the command times out, your route table association is incorrect or the NAT Gateway is not yet in Available state.

Common misconfiguration: Associating the wrong AZ’s private subnet with the wrong AZ’s NAT Gateway route table. This means cross-AZ NAT traffic, which works but defeats the HA design and incurs cross-AZ data transfer charges.

Route table associations showing three tiers

In this section, I confirmed:

0 of 5 completed

Route Table Validation

Question 1 of 2

0/2

A private EC2 instance sends a request to 8.8.8.8. What is the correct traffic path?

Choose your language

Select your preferred language for the site