AWS VPC Networking: Subnets, Route Tables, and Internet Gateways Demystified

When you spin up EC2 instances in AWS, they need to live somewhere. That somewhere is a VPC (Virtual Private Cloud) - your own isolated network in the AWS cloud.

The VPC Basics

A VPC is just a chunk of IP addresses you define. Pick a CIDR block like 10.0.0.0/16 and you get 65,536 IP addresses to work with.

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  
  tags = {
    Name = "production-vpc"
  }
}

But a VPC alone doesn’t do much. You need subnets, route tables, and gateways to make it useful.

Subnets: Public vs Private

Subnets divide your VPC into smaller networks. The key distinction is whether they can talk to the internet:

Public Subnet: Has a route to an Internet Gateway, resources get public IPs Private Subnet: No internet access, only internal communication

# Public subnet for web servers
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "us-east-1a"
  
  map_public_ip_on_launch = true
}

# Private subnet for databases
resource "aws_subnet" "private" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.2.0/24"
  availability_zone = "us-east-1a"
}

Internet Gateway: The Front Door

An Internet Gateway (IGW) lets resources in your VPC talk to the internet. Attach it to your VPC:

resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.main.id
}

But the IGW alone isn’t enough - you need routes.

Route Tables: Traffic Cops

Route tables tell traffic where to go. The default route table keeps everything internal. You need to add a route pointing to the IGW:

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block = "0.0.0.0/0"  # All internet traffic
    gateway_id = aws_internet_gateway.igw.id
  }
}

# Associate with public subnet
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}

Now anything in the public subnet can reach the internet and vice versa.

NAT Gateway: Private Subnet Internet Access

What if your private subnet needs to download updates but shouldn’t be reachable from the internet? That’s where NAT Gateway comes in:

# NAT Gateway needs a public IP
resource "aws_eip" "nat" {
  domain = "vpc"
}

resource "aws_nat_gateway" "nat" {
  allocation_id = aws_eip.nat.id
  subnet_id     = aws_subnet.public.id  # Lives in public subnet
}

# Private subnet route table points to NAT
resource "aws_route_table" "private" {
  vpc_id = aws_vpc.main.id
  
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.nat.id
  }
}

Traffic flow: Private instance → NAT Gateway → Internet Gateway → Internet

The NAT Gateway acts as a middleman, making outbound requests on behalf of private instances but blocking inbound traffic.

Security Groups: Stateful Firewalls

Security groups control what traffic can reach your instances. They’re stateful - if you allow incoming traffic on port 80, the response automatically gets through:

resource "aws_security_group" "web" {
  vpc_id = aws_vpc.main.id
  
  # Allow HTTP from anywhere
  ingress {
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  
  # Allow all outbound
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Network ACLs: Stateless Backup

Network ACLs are subnet-level firewalls. Unlike security groups, they’re stateless - you need explicit rules for inbound AND outbound:

resource "aws_network_acl" "main" {
  vpc_id     = aws_vpc.main.id
  subnet_ids = [aws_subnet.public.id]
  
  # Allow HTTP in
  ingress {
    rule_no    = 100
    protocol   = "tcp"
    from_port  = 80
    to_port    = 80
    cidr_block = "0.0.0.0/0"
    action     = "allow"
  }
  
  # Allow HTTP responses out
  egress {
    rule_no    = 100
    protocol   = "tcp"
    from_port  = 1024
    to_port    = 65535
    cidr_block = "0.0.0.0/0"
    action     = "allow"
  }
}

The Mental Model

Think of it like a building:

  • VPC: The entire building
  • Subnets: Individual floors
  • Internet Gateway: Main entrance
  • NAT Gateway: Service entrance (outbound only)
  • Route Tables: Elevator directories
  • Security Groups: Door locks on each room
  • NACLs: Security guard at each floor

Once you understand how traffic flows through these components, AWS networking clicks. Start with this basic setup, then add complexity as needed - VPC peering, VPN connections, Transit Gateways. But this foundation covers 90% of use cases.