AWS for DevOps — Internet & NAT Gateways

4 minute read

4.3 — Internet & NAT Gateways

An Internet Gateway (IGW) connects your VPC to the internet. A NAT Gateway lets resources in private subnets access the internet without being directly reachable from outside.

Internet Gateway

  Internet
     │
     ▼
  ┌──────────────┐
  │ Internet     │    1:1 mapping with a VPC
  │ Gateway      │    Highly available by design
  │ (igw-abc123) │    No bandwidth limits
  └──────┬───────┘
         │
  ┌──────┴────── VPC ──────────────────────┐
  │  Public Subnet                          │
  │  Route: 0.0.0.0/0 → igw-abc123        │
  │  ┌────────┐                             │
  │  │ EC2    │ ← has public IP             │
  │  └────────┘                             │
  └─────────────────────────────────────────┘

Creating an Internet Gateway

# Create the IGW
$ aws ec2 create-internet-gateway \
    --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=prod-igw}]'
{
    "InternetGateway": {
        "InternetGatewayId": "igw-abc123"
    }
}

# Attach it to the VPC
$ aws ec2 attach-internet-gateway \
    --internet-gateway-id igw-abc123 \
    --vpc-id vpc-0abc123

# Add a route in the public route table
$ aws ec2 create-route \
    --route-table-id rtb-pub123 \
    --destination-cidr-block 0.0.0.0/0 \
    --gateway-id igw-abc123

For an instance in a public subnet to reach the internet, it needs:

  1. ✅ A route to the IGW in its route table
  2. ✅ A public IP (or Elastic IP)
  3. ✅ A security group that allows the traffic

NAT Gateway

Private subnets have no route to the IGW, so instances inside cannot reach the internet directly. But they often need to — for software updates, pulling Docker images, or calling external APIs.

A NAT Gateway solves this:

  Internet
     │
     ▼
  ┌──── IGW ────┐
  │             │
  │  ┌─────────┴───── Public Subnet ──────┐
  │  │  NAT Gateway (nat-def456)           │
  │  │  Elastic IP: 52.66.100.200          │
  │  └──────────────┬─────────────────────┘
  │                 │
  │  ┌──────────────┴── Private Subnet ───┐
  │  │  Route: 0.0.0.0/0 → nat-def456     │
  │  │  ┌────────┐ ┌────────┐             │
  │  │  │ EC2    │ │ ECS    │             │
  │  │  │ app-01 │ │ task   │             │
  │  │  └────────┘ └────────┘             │
  │  └────────────────────────────────────┘
  └────────────────────────────────────────┘

How it works:

  1. App server sends a request to https://api.github.com
  2. Traffic goes to NAT Gateway (via route table)
  3. NAT Gateway translates the private IP to its Elastic IP
  4. Request goes through the IGW to the internet
  5. Response comes back through IGW → NAT → app server

Key property: Outside servers see the NAT Gateway’s Elastic IP, not the app server’s private IP. The app server is not reachable from the internet.

Creating a NAT Gateway

# Step 1 — Allocate an Elastic IP for the NAT Gateway
$ aws ec2 allocate-address --domain vpc
{
    "AllocationId": "eipalloc-nat123",
    "PublicIp": "52.66.100.200"
}

# Step 2 — Create the NAT Gateway in a PUBLIC subnet
$ aws ec2 create-nat-gateway \
    --subnet-id subnet-pub1a \
    --allocation-id eipalloc-nat123 \
    --tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=nat-1a}]'
{
    "NatGateway": {
        "NatGatewayId": "nat-def456",
        "State": "pending"
    }
}

# Wait for it to be available
$ aws ec2 wait nat-gateway-available --nat-gateway-ids nat-def456

# Step 3 — Add a route in the private route table
$ aws ec2 create-route \
    --route-table-id rtb-prv123 \
    --destination-cidr-block 0.0.0.0/0 \
    --nat-gateway-id nat-def456

High Availability for NAT

A single NAT Gateway lives in one AZ. If that AZ goes down, private subnets in other AZs lose internet access. For production, create one NAT Gateway per AZ:

  AZ-a                          AZ-b
  ┌── Public Subnet ──┐        ┌── Public Subnet ──┐
  │  NAT-a             │        │  NAT-b             │
  └────────┬───────────┘        └────────┬───────────┘
           │                             │
  ┌── Private Subnet ─┐        ┌── Private Subnet ─┐
  │  Route → NAT-a     │        │  Route → NAT-b     │
  │  App servers       │        │  App servers       │
  └────────────────────┘        └────────────────────┘
# NAT Gateway in AZ-b
$ aws ec2 allocate-address --domain vpc
$ aws ec2 create-nat-gateway \
    --subnet-id subnet-pub1b \
    --allocation-id eipalloc-nat456

# Separate route table for private subnets in AZ-b
$ aws ec2 create-route \
    --route-table-id rtb-prv-1b \
    --destination-cidr-block 0.0.0.0/0 \
    --nat-gateway-id nat-ghi789

NAT Gateway Costs

Component Cost (ap-south-1, approximate)
Hourly charge ~$0.045/hour (~$33/month)
Data processing ~$0.045/GB

Cost tip: NAT Gateways are one of the biggest hidden costs on AWS. For dev/test, consider a NAT instance (a small EC2 running NAT) or use VPC endpoints to avoid NAT for AWS service calls.

IGW vs NAT Gateway Comparison

Feature Internet Gateway NAT Gateway
Direction Bidirectional (inbound + outbound) Outbound only
Placed in VPC level Public subnet
Cost Free ~$33/month + data
Used by Public subnet resources Private subnet resources
Public IP needed Yes (on the instance) Yes (Elastic IP on NAT)
High availability Built-in One per AZ for HA

Common Mistakes

Mistake Why It Hurts Fix
Placing NAT Gateway in a private subnet NAT itself can’t reach the internet Always create NAT in a public subnet
Only one NAT Gateway for all AZs AZ failure affects all private subnets One NAT per AZ
Not realising NAT costs add up Surprise bills Monitor data processing, use VPC endpoints
Forgetting the Elastic IP for NAT NAT creation fails Allocate EIP first

Quick Reference

Task Command
Create IGW aws ec2 create-internet-gateway
Attach IGW aws ec2 attach-internet-gateway --internet-gateway-id IGW --vpc-id VPC
Create NAT aws ec2 create-nat-gateway --subnet-id SUBNET --allocation-id EIP
Route to IGW aws ec2 create-route --route-table-id RT --destination-cidr-block 0.0.0.0/0 --gateway-id IGW
Route to NAT aws ec2 create-route --route-table-id RT --destination-cidr-block 0.0.0.0/0 --nat-gateway-id NAT
Delete NAT aws ec2 delete-nat-gateway --nat-gateway-id NAT

Next → Security Groups vs NACLs

Updated: