Laptop to Production

Routes, gateways and NAT

How traffic finds its way in and out.

Time
50 minutes
Level
Beginner

Part 1: The problem

QuickCart, the first bill after launch

Sam moved QuickCart’s API servers into private subnets, exactly as planned. The next morning, a security update wouldn’t install. The command just sat there, printing nothing, until it gave up. The servers had no way out to the internet at all.

Sam added a NAT gateway and updates worked again. A month later the bill arrived, with a large line for “NAT gateway data processing”. Every night, the servers copied the day’s receipts to S3 — and every gigabyte of it went through the NAT gateway, which charges for each one.

Both of Sam’s problems are about routing: where traffic is allowed to go, and which way it takes to get there.

Part 2: The idea

Every subnet has a route table: a list of rules. Each rule says traffic for these addresses goes to this target.

DestinationTargetMeans
10.0.0.0/16localAnything inside the VPC: deliver it inside the VPC
0.0.0.0/0an internet gatewayEverything else: out to the internet, both ways
0.0.0.0/0a NAT gatewayEverything else: out through the NAT gateway, one way
S3’s prefix lista gateway endpointAnything for S3: through the private endpoint

The VPC lesson showed the one line that makes a subnet public. This lesson is about everything else in the table.

The local route

Every route table starts with a route for the VPC’s own range, with the target local. You can’t delete it or change its target. It’s why every server in a VPC can reach every other one, whatever else the table says. (Whether they’re allowed to talk is a job for security groups.)

The most specific route wins

A packet often matches more than one route. 0.0.0.0/0 matches every address. 10.0.0.0/16 matches only the VPC. The rule is: the most specific match wins — the route with the longest prefix, the biggest number after the slash.

Packet going toMatching routesChosen
10.0.21.9 (the database)10.0.0.0/16 and 0.0.0.0/010.0.0.0/16 → local. /16 is more specific than /0
An address on the internetonly 0.0.0.0/00.0.0.0/0
An S3 addressS3’s prefix list and 0.0.0.0/0The prefix list. It names S3’s exact ranges

This is why you can add a special route without breaking the general one.

Which table does a subnet use?

Each subnet is associated with exactly one route table. Every VPC comes with a main route table, and any subnet you don’t associate explicitly uses it.

TableWhat it’s forGood practice
MainThe fallback for every subnet not given another tableLeave it with only local, so a new subnet starts isolated, not public by accident
CustomOne per kind of subnet: public, app, dataAssociate every subnet on purpose

The internet gateway: two-way, one per VPC

An internet gateway is the VPC’s door to the internet. There is one per VPC, it scales by itself, and it costs nothing. Traffic flows both ways through it — but only for servers that also have a public IP address. A server with only a private address can’t use it, even if the route is there.

The NAT gateway: one-way, out only

Private servers still need to reach out: to download updates, to call a payment provider. A NAT gateway gives them a way out without a way in.

It sits in a public subnet. Private subnets send 0.0.0.0/0 to it. It swaps each server’s private address for its own public one, sends the request on through the internet gateway, and passes the reply back. Nothing on the internet can start a connection to the servers behind it.

NAT gateway factWhat it means for you
Needs an Elastic IPA fixed public address that stays yours until you release it. The NAT gateway uses it as the address the internet sees
Lives in one zoneIf that zone fails, the NAT gateway goes with it
Costs money every hour, plus every gigabyteIn us-east-1, about $0.045 an hour and about $0.045 per GB processed at the time of writing. Prices change; check the VPC pricing page
Can’t be stoppedOnly deleted. It bills every hour it exists

One per zone, or one for all? Production puts a NAT gateway in each zone, and each zone’s private subnets route to their own. Lose a zone, and the other zone still has its way out. A test environment often shares one NAT gateway to halve the cost, accepting that losing that zone cuts everyone off. AWS has also recently added a regional mode that spreads a NAT gateway across zones for you; this lesson uses the long-standing zonal kind, which is what you’ll meet in most existing setups.

Key point: The route table decides where traffic goes: local inside the VPC, an internet gateway for two-way, a NAT gateway for out-only, an endpoint for a private path to an AWS service.

VPC endpoints: reaching AWS without the internet

S3, Secrets Manager, ECR and the other AWS services live outside your VPC, on public addresses. A VPC endpoint gives your subnets a private path to one of them instead.

Gateway endpointInterface endpoint
ForS3 and DynamoDB onlyMost other services: Secrets Manager, ECR, CloudWatch, Systems Manager
How it worksAdds a route to your route tables: S3’s prefix list → the endpointPuts a network interface with a private address in your subnets
CostFreeAbout $0.01 an hour per zone, plus data, at the time of writing
Why use itCuts NAT data charges; S3 traffic never leaves AWS’s networkPrivate servers reach the service with no NAT gateway at all

A prefix list is a named, AWS-maintained list of a service’s address ranges, shown as an ID starting pl-. You’ll see it as the destination of the endpoint’s route.

Part 3: See it

Below is one subnet with one API server, and its route table. It starts with only the local route. Add and remove routes, and send the three kinds of traffic each time. Watch the label on the subnet change.

Add a route
Send traffic
The internet on the left. An internet gateway leads into QuickCart's VPC. Inside, a public subnet holds a NAT gateway, and the subnet you are editing holds an API server. On the right, outside the VPC, is S3, reached through a gateway endpoint on the VPC's edge.The internetInternetvisitors, updatesQuickCart VPC (10.0.0.0/16)Public subnet0.0.0.0/0 → internet gatewayNAT gatewayThe subnet you're editingIsolatedAPI server10.0.11.24internetgatewayendpointAWS servicesS3menu photos
Route table for the subnet you're editing
DestinationTargetAction
IsolatedThe table has only the local route. The server can talk to the rest of the VPC and nothing else. Add a route, then send some traffic.

What to notice: nothing about the server changed — only the table. The same server is public, private or isolated depending on one route, and S3 traffic switches to the endpoint the moment its more specific route exists.

Part 4: Do it

You’ll build a public subnet with a NAT gateway, a private subnet that uses it, and an S3 gateway endpoint, then prove a private server can reach out. About 40 minutes. Region: N. Virginia (us-east-1).

  1. Create the VPC and two subnets

    In the AWS console: VPCYour VPCsCreate VPC

    FieldEnter
    Resources to createVPC only
    Name tagquickcart-vpc
    IPv4 CIDR blockIPv4 CIDR manual input
    IPv4 CIDR10.0.0.0/16
    IPv6 CIDR blockNo IPv6 CIDR block
    TenancyDefault

    Choose Create VPC. Then:

    In the AWS console: VPCSubnetsCreate subnet

    Choose quickcart-vpc, and add two subnets:

    Subnet nameAvailability ZoneIPv4 subnet CIDR block
    quickcart-public-aus-east-1a10.0.1.0/24
    quickcart-app-aus-east-1a10.0.11.0/24

    Choose Create subnet.

    You should see

    Two subnets. Both use the main route table, which has only the local route: both are isolated for now.

  2. Make the public subnet public

    In the AWS console: VPCInternet gatewaysCreate internet gateway

    FieldEnter
    Name tagquickcart-igw

    Choose Create internet gateway, then Actions → Attach to VPC, pick quickcart-vpc, and choose Attach internet gateway.

    In the AWS console: VPCRoute tablesCreate route table

    FieldEnter
    Namequickcart-rt-public
    VPCquickcart-vpc

    Choose Create route table. On Routes, choose Edit routes → Add route:

    DestinationTarget
    0.0.0.0/0Internet Gateway, then quickcart-igw

    Choose Save changes. On Subnet associations, choose Edit subnet associations, tick quickcart-public-a, and choose Save associations.

    You should see

    quickcart-rt-public with two routes, 10.0.0.0/16 → local and 0.0.0.0/0 → igw-…, and one associated subnet.

  3. Create the NAT gateway in the public subnet

    In the AWS console: VPCNAT gatewaysCreate NAT gateway

    FieldEnter
    Namequickcart-nat-a
    Availability mode (if the form shows it)Zonal
    Subnetquickcart-public-a — the public one
    Connectivity typePublic
    Elastic IP allocation IDChoose Allocate Elastic IP

    Choose Create NAT gateway. The clock on its cost starts now.

    You should see

    State: Pending, changing to Available after a minute or two. The details show a Primary public IPv4 address: the Elastic IP the internet will see.

  4. Route the private subnet through the NAT gateway

    In the AWS console: VPCRoute tablesCreate route table

    FieldEnter
    Namequickcart-rt-app
    VPCquickcart-vpc

    Choose Create route table. On Routes, choose Edit routes → Add route:

    DestinationTarget
    0.0.0.0/0NAT Gateway, then quickcart-nat-a

    Save, then on Subnet associations associate quickcart-app-a.

    You should see

    quickcart-rt-app with 10.0.0.0/16 → local and 0.0.0.0/0 → nat-…. The app subnet is now private: out only.

  5. Add the S3 gateway endpoint

    In the AWS console: VPCEndpointsCreate endpoint

    FieldEnter
    Name tagquickcart-s3
    Type (or Service category)AWS services
    ServicesSearch s3, and choose com.amazonaws.us-east-1.s3 with type Gateway
    VPCquickcart-vpc
    Route tablesTick quickcart-rt-app
    PolicyFull access

    Choose Create endpoint.

    You should see

    Open quickcart-rt-app again. It now has a third route: destination pl-… (S3’s prefix list), target vpce-…. You never typed it — creating the endpoint added it. From now on S3 traffic from the app subnet skips the NAT gateway.

  6. Launch a test server in the private subnet

    This server has no public address and no way to log in. It will check the internet as it boots and write the answer to its system log.

    In the AWS console: EC2InstancesLaunch instances

    FieldEnter
    Namequickcart-route-test
    Application and OS ImagesAmazon Linux 2023 — the default
    Instance typet3.micro
    Key pairProceed without a key pair
    Network settingsChoose Edit
    VPCquickcart-vpc
    Subnetquickcart-app-a
    Auto-assign public IPDisable
    Firewall (security groups)Create security group, and untick Allow SSH traffic from. Nothing needs to connect in

    Open Advanced details. In User data, paste this script:

    user data
    #!/bin/bash
    if curl --silent --max-time 10 --output /dev/null https://example.com; then
      echo "QUICKCART CHECK: the internet is reachable"
    else
      echo "QUICKCART CHECK: the internet is NOT reachable"
    fi
    PartWhat it means
    #!/bin/bashrun this script with bash. User data runs once, as root, on first boot
    if … then … else … firun the first echo if the command succeeds, the second if it fails
    curlfetch a web address
    --silentdon’t print a progress bar
    --max-time 10give up after 10 seconds instead of hanging
    --output /dev/nullthrow the page away; only success or failure matters
    https://example.coma public test site; any public HTTPS address would do
    echo "QUICKCART CHECK: …"print a line that’s easy to spot in the log

    Choose Launch instance.

  7. Read the answer in the system log

    Wait until the instance shows Running and its status checks have passed — a few minutes.

    In the AWS console: EC2Instancesquickcart-route-testActionsMonitor and troubleshootGet system log

    Scroll near the end, or use your browser’s find, for QUICKCART CHECK.

    Among the boot messages
    QUICKCART CHECK: the internet is reachable

    The log can take a few minutes to fill in; if the line isn’t there, wait and choose Get system log again. The lines around it will differ from anyone else’s.

    You should see

    A server with no public address reached the internet: out through quickcart-nat-a, through quickcart-igw, and back. Nothing on the internet can reach it.

  8. Clean-up step: Clean up

    Do these in order. Each one frees what the next one needs.

    1. Terminate the test server.

    In the AWS console: EC2Instancesquickcart-route-testInstance stateTerminate (delete) instance

    2. Delete the endpoint.

    In the AWS console: VPCEndpointsquickcart-s3ActionsDelete VPC endpoints

    Type delete and confirm.

    3. Delete the NAT gateway, and wait.

    In the AWS console: VPCNAT gatewaysquickcart-nat-aActionsDelete NAT gateway

    Type delete and confirm. Refresh until State is Deleted. It takes a few minutes.

    4. Release the Elastic IP. This is the step people forget.

    In the AWS console: VPCElastic IPsActionsRelease Elastic IP addresses

    Tick the address the NAT gateway used, then choose Release.

    5. Delete the VPC.

    In the AWS console: VPCYour VPCsquickcart-vpcActionsDelete VPC

    Type delete and choose Delete. If it complains that something is still in use, the instance is still terminating; wait a minute and try again.

    You should see

    No NAT gateways except the deleted one, no Elastic IPs in the list, no running instance, and no quickcart-vpc. Nothing is left billing.

Part 5: Check yourself

A subnet's route table has 10.0.0.0/16 → local and 0.0.0.0/0 → nat-gateway. What kind of subnet is it?

The same table also has a route for S3's prefix list to a gateway endpoint. Where does a request to S3 go?

Where must a NAT gateway be created?

QuickCart runs in two zones with one NAT gateway, in zone a. Zone a fails. What happens to servers in zone b's private subnet?

You're cleaning up. What order works?

Part 6: Cheat sheet

On one card
TermIn one line
Route tableDestination → target rules for a subnet. One table per subnet
localThe VPC’s own range. Always there, can’t be removed
Most specific winsOf all matching routes, the one with the biggest number after the slash
Main route tableUsed by any subnet not associated with another. Keep it isolated
Public subnet0.0.0.0/0 → igw. Servers also need a public IP
Private subnet0.0.0.0/0 → nat. Out only
Isolated subnetNo 0.0.0.0/0 route
Internet gatewayOne per VPC, two-way, free
NAT gatewayIn a public subnet, one zone, needs an Elastic IP, bills hourly and per GB
High availabilityOne NAT gateway per zone, each zone routing to its own
Gateway endpointS3 and DynamoDB. A route, free. Cuts NAT charges
Interface endpointOther services. A private address in your subnet, bills hourly
Clean-up orderNAT gateway → wait for Deleted → release Elastic IP → VPC

Finished the build and the quiz? Mark it done to fill in its stop on the route map.