Skip to main content

Amazon VPC - Subnets, Route Tables, Security Groups, and NAT Gateways

Design and build a production VPC with public and private subnets, Internet Gateway, NAT Gateway, Security Groups, and VPC Flow Logs from scratch.

What you will learn

  • What a VPC is and why everything in AWS lives inside one
  • How CIDR blocks and subnets carve up your network — and the 5 IPs AWS takes from every subnet
  • What makes a subnet public vs private — the exact three things required
  • How Bastion Hosts let you SSH into private servers
  • How NAT Gateway gives private servers internet access without exposing them
  • Security Groups vs NACLs — stateful vs stateless in plain language
  • VPC Endpoints — how to reach S3 without going through the internet
  • VPC Flow Logs for debugging network problems

Why this matters

Every resource you create in AWS — EC2, RDS, Lambda, ECS — lives inside a VPC. Get VPC wrong and you either expose your production database to the internet (a security disaster) or lock things down so tightly your application cannot reach the services it needs (an operational disaster).

At Swiggy, a misconfigured NACL during a network change silently dropped 30% of API traffic for 12 minutes. VPC Flow Logs identified the culprit in minutes. At Razorpay, routing S3 traffic through NAT Gateway instead of a VPC Endpoint was adding significant cost every month for traffic that never needed to touch the internet.

VPC is not a one-time setup. It is the foundation every other service sits on.

What is a VPC

A VPC (Virtual Private Cloud) is your own isolated private network inside AWS — completely separate from every other AWS customer. When you launch an EC2 instance or an RDS database, it lives inside your VPC and gets a private IP address that only resources inside the same VPC can reach by default.

◈ DIAGRAM
AWS Account
└── VPC (your private network)
├── Subnet A (Availability Zone 1a)
│ └── EC2 instances, RDS, Lambda...
└── Subnet B (Availability Zone 1b)
└── EC2 instances, RDS, Lambda...

When you open a new AWS account, AWS creates a default VPC in every region with everything pre-configured. It is fine for experimenting. In production, you create your own custom VPC and control everything yourself.

CIDR Blocks and Subnets

A VPC has a CIDR block — the pool of IP addresses your entire network can use. You pick this when creating the VPC.

AWS only allows three private IP ranges inside a VPC:

◈ DIAGRAM
10.0.0.0/8 → 10.0.0.0 to 10.255.255.255
172.16.0.0/12 → 172.16.0.0 to 172.31.255.255
192.168.0.0/16 → 192.168.0.0 to 192.168.255.255

A common production choice is 10.0.0.0/16 which gives you 65,536 IP addresses to distribute across subnets.

Subnets divide your VPC into smaller pieces

You cannot launch EC2 instances directly into a VPC. You launch them into subnets. A subnet is a slice of the VPC CIDR block, locked to one Availability Zone.

◈ DIAGRAM
VPC: 10.0.0.0/16 (65,536 addresses)
├── Subnet A (10.0.1.0/24) in ap-south-1a → 256 addresses
├── Subnet B (10.0.2.0/24) in ap-south-1b → 256 addresses
├── Subnet C (10.0.10.0/24) in ap-south-1a → 256 addresses
└── Subnet D (10.0.11.0/24) in ap-south-1b → 256 addresses

The 5 reserved IPs — AWS takes these in every subnet

Using 10.0.1.0/24 as an example:

Address Reserved for
10.0.1.0 Network address — identifies the subnet itself
10.0.1.1 VPC Router — handles routing inside the VPC
10.0.1.2 AWS DNS server
10.0.1.3 Reserved by AWS for future use
10.0.1.255 Broadcast address

A /24 subnet has 256 total addresses. Subtract 5 reserved = 251 usable.

Remember

Always size subnets bigger than you think you need. Once created, a subnet cannot be resized. If you need 29 EC2 instances and pick a /27 (32 - 5 = 27 usable), you are already stuck. Pick a /26 (64 - 5 = 59 usable) and have breathing room.

What Makes a Subnet Public or Private

This confuses a lot of engineers. A subnet does not magically become public just by existing. Three specific things must all be true for a subnet to be reachable from the internet.

◈ DIAGRAM
A subnet is PUBLIC only if ALL THREE are true:
1. The VPC has an Internet Gateway attached
2. The subnet's Route Table has a route: 0.0.0.0/0 → Internet Gateway
3. The EC2 instance has a public IP or Elastic IP
A subnet is PRIVATE if any one of those is missing
Public Route Table:
Destination Target
10.0.0.0/16 local ← stays inside VPC
0.0.0.0/0 igw-abc123 ← everything else goes to internet
Private Route Table:
Destination Target
10.0.0.0/16 local ← stays inside VPC
0.0.0.0/0 nat-abc123 ← everything else goes to NAT Gateway

Production setup always follows this pattern:

◈ DIAGRAM
Public subnets → Load Balancers, NAT Gateways, Bastion Hosts
Private subnets → EC2 app servers, RDS databases, ElastiCache

Your EC2 instances and databases should never be directly reachable from the internet. Only the Load Balancer sits in the public subnet.

Common Mistake

Creating an Internet Gateway and attaching it to the VPC but forgetting to add the 0.0.0.0/0 → IGW route to the Route Table. The gateway exists but traffic has no route to use it. Public instances cannot reach the internet and you spend 20 minutes wondering why. Always check Route Tables first when debugging connectivity.

Bastion Host — SSH into Private Instances

Your EC2 instances are in private subnets. They have no public IP. You cannot SSH into them directly from the internet. A Bastion Host solves this.

A Bastion Host is a small EC2 instance in the public subnet that acts as a jump server — the only entry point for SSH access.

◈ DIAGRAM
Your laptop
↓ SSH on port 22
Bastion Host (public subnet, has public IP)
↓ SSH on port 22
Private EC2 (private subnet, no public IP)

Security Group rules:

TEXT
Bastion Security Group:
Inbound: Allow port 22 from YOUR specific IP address only
Outbound: Allow port 22 to Private EC2 Security Group
Private EC2 Security Group:
Inbound: Allow port 22 from Bastion Security Group ID
(reference the SG itself, not an IP address)
Security

Never open SSH (port 22) from 0.0.0.0/0 on any Security Group — not even the Bastion. Automated scanners try port 22 on every public IP in the world within minutes of an instance launching. Restrict SSH to your specific IP only.

NAT Gateway — Private Servers Reaching the Internet

Private EC2 instances often need to reach the internet — to pull OS updates, download packages, call external payment APIs. But they cannot have a public IP or anyone could reach them.

NAT Gateway solves this. It sits in the public subnet and acts as a middleman.

◈ DIAGRAM
Private EC2 (10.0.10.5) wants to reach api.razorpay.com
↓
Request goes to NAT Gateway in public subnet
↓
NAT Gateway replaces source IP with its own Elastic IP (e.g. 13.235.45.67)
↓
Request reaches the internet
↓
Response comes back to NAT Gateway's Elastic IP
↓
NAT Gateway forwards it back to Private EC2 (10.0.10.5)

The internet sees the NAT Gateway's IP, never the private EC2 IP. The private instance stays invisible.

Key facts about NAT Gateway:

Property Value
Where it lives Public subnet (always)
Needs An Elastic IP
Managed by AWS — no patching needed
Scales automatically Up to 100 Gbps
Cost Per hour + per GB processed
High Availability Create one per AZ

One NAT Gateway per AZ — not just one total

◈ DIAGRAM
Wrong — one NAT Gateway for everything:
AZ-1a private instances → NAT GW (in AZ-1a) → internet ✓
AZ-1b private instances → NAT GW (in AZ-1a) → internet ✗ cross-AZ traffic costs money
If AZ-1a goes down → AZ-1b private instances lose internet entirely
Right — one NAT Gateway per AZ:
AZ-1a private instances → NAT GW A (in AZ-1a) → internet
AZ-1b private instances → NAT GW B (in AZ-1b) → internet
If AZ-1a goes down → AZ-1b continues working normally

Security Groups vs NACLs

Both control traffic but at different levels with different behaviours.

Security Groups — attached to instances, stateful

A Security Group is a firewall attached directly to EC2 instances, RDS databases, Lambda functions, and other resources. Allow rules only — there is no explicit deny.

Stateful means return traffic is automatically allowed:

◈ DIAGRAM
User sends request to your server on port 443
↓
Security Group checks inbound rule → port 443 allowed → request passes
↓
Server sends response back
↓
Response automatically allowed outbound — no separate rule needed

Security Group chaining:

◈ DIAGRAM
Internet
↓ allow 80, 443 from 0.0.0.0/0
ALB Security Group
↓ allow 8080 from ALB Security Group ID
App EC2 Security Group
↓ allow 5432 from App EC2 Security Group ID
RDS Security Group

The database only accepts connections from the app servers, not from the internet. The app servers only accept connections from the load balancer. The source is a Security Group ID — not an IP address. IPs change when instances restart. Security Group IDs never change.

NACLs — attached to subnets, stateless

Network ACLs apply to all traffic entering or leaving a subnet. They have both allow and deny rules. Rules are evaluated by number — lowest wins.

Stateless means return traffic is checked separately:

◈ DIAGRAM
Request arrives on port 443 → inbound rule checked → ALLOWED
Response leaves on ephemeral port 54321 → outbound rule checked separately
If no outbound rule allows port 54321 → response BLOCKED

This is the ephemeral port trap. When a client connects to your server, the response goes back to a random high port (1024-65535) that the client chose. NACLs must explicitly allow outbound traffic on the full ephemeral port range.

TEXT
NACL outbound rules for a web server:
Allow TCP port 443 outbound
Allow TCP ports 1024-65535 outbound (ephemeral ports)

Quick comparison:

Security Group NACL
Applied to Instance Subnet
Rules Allow only Allow and Deny
Stateful Yes No
Return traffic Auto-allowed Must explicitly allow
Block a specific IP Cannot Yes — add a Deny rule
Rule evaluation All rules together Lowest number first
Remember

Use NACLs when you need to block a specific IP address — Security Groups cannot do explicit deny. For everything else, Security Groups are simpler and correct. NACLs are a second layer of defence, not a replacement.

VPC Endpoints — Private Path to AWS Services

Without a VPC Endpoint, your private EC2 instances accessing S3 travel this path:

◈ DIAGRAM
Private EC2 → NAT Gateway → internet → S3

You pay NAT Gateway charges for traffic that never needed to touch the internet. S3 is an AWS service. AWS has a direct private path to it.

With a VPC Endpoint:

◈ DIAGRAM
Private EC2 → VPC Endpoint → S3 (entirely inside AWS network)

Two types of VPC Endpoints:

Gateway Endpoint Interface Endpoint
Services S3 and DynamoDB only Most other AWS services
Cost Free Per hour + per GB
How it works Entry added to your Route Table Private IP created in your subnet
Tip

Create Gateway Endpoints for S3 and DynamoDB in every VPC from day one. They are completely free, more secure (traffic never leaves AWS), and eliminate NAT Gateway charges for S3 and DynamoDB traffic. For a data-heavy workload this can save significant money every month.

VPC Flow Logs — See All Network Traffic

VPC Flow Logs record every accepted and rejected connection in your VPC. They do not affect traffic — they only observe it.

Three things every Flow Log record tells you:

◈ DIAGRAM
srcaddr → where the traffic came from
dstaddr → where it was going
action → ACCEPT or REJECT

This is how you debug network problems:

◈ DIAGRAM
Something is blocked but you cannot figure out why
↓
Check Flow Logs and filter for REJECT
↓
REJECT on inbound → Security Group or NACL blocking incoming traffic
REJECT on outbound → Security Group or NACL blocking the response
ACCEPT inbound but REJECT outbound → stateless NACL blocking response
→ add ephemeral port outbound rule

Flow Logs can go to CloudWatch Logs for real-time alerting or S3 for archiving and Athena analysis.

NAT Instance — The Old Way (and Why Source/Destination Check Matters)

Before NAT Gateway existed, teams used NAT Instances — regular EC2 instances configured to forward traffic on behalf of private subnet instances. Today NAT Gateway is always the better choice. But one concept from NAT Instance is tested everywhere: Source/Destination Check.

By default, every EC2 instance checks that it is the actual source or destination of any packet it receives. If the packet is not addressed to it, the instance drops it.

A NAT Instance needs to forward packets that are addressed to other destinations — that is its entire job. So Source/Destination Check must be disabled on a NAT Instance, otherwise it silently drops all the traffic it is supposed to forward.

◈ DIAGRAM
NAT Instance with Source/Destination Check ON (wrong):
Private EC2 sends packet to 8.8.8.8 via NAT Instance
NAT Instance receives packet → checks if it is addressed to itself → no → drops it
Private EC2 cannot reach the internet
NAT Instance with Source/Destination Check OFF (correct):
Private EC2 sends packet to 8.8.8.8 via NAT Instance
NAT Instance receives packet → does not check → forwards it
Private EC2 reaches the internet

NAT Gateway handles this automatically. You never think about it. NAT Instance requires you to disable it manually.

NAT Gateway NAT Instance
Managed by AWS — fully managed You — patch it, size it, monitor it
Availability Highly available in its AZ Manual setup with ASG needed
Bandwidth Up to 100 Gbps, auto-scales Limited by instance type
Source/Destination Check Not applicable Must be disabled manually
Use as Bastion No Yes
Cost Per hour + per GB EC2 instance cost

Use NAT Gateway for everything new. NAT Instance is legacy.

VPC Peering — Connect Two VPCs Directly

VPC Peering creates a direct private connection between two VPCs. Traffic flows over AWS's private network — not the internet.

◈ DIAGRAM
VPC A (10.0.0.0/16) ←——peering connection——→ VPC B (172.16.0.0/16)
EC2 in VPC A can reach EC2 in VPC B using private IPs
No internet involved

Two rules that catch engineers:

No transitive peering:

TEXT
VPC A peered with VPC B
VPC B peered with VPC C
Can VPC A reach VPC C through VPC B?
Answer: No. Peering is not transitive.
To connect A and C — create a direct peering between A and C.

No overlapping CIDRs:

◈ DIAGRAM
VPC A: 10.0.0.0/16
VPC B: 10.0.0.0/16 ← same range
Cannot peer — AWS cannot route between identical address spaces
Fix: Plan CIDRs before creating VPCs.
If you need to peer later, non-overlapping ranges are a hard requirement.

For connecting many VPCs at scale, Transit Gateway is cleaner than maintaining many peering connections between every pair.

Networking Cost — What Is Free and What Is Not

AWS charges for data transfer in ways that surprise teams. Understanding the rules saves real money.

◈ DIAGRAM
Free:
Traffic within the same AZ using private IP addresses → always free
Traffic from internet into AWS (ingress) → free
Traffic between services using private IPs in the same AZ → free
Charged:
Traffic between AZs → $0.01 per GB each way
Traffic going out to the internet → $0.09 per GB (first 10 TB/month)
NAT Gateway processing → $0.045 per GB
Example — EC2 to RDS in different AZs:
EC2 in ap-south-1a → RDS in ap-south-1b
Every GB transferred costs $0.02 (both directions count)
Fix: run EC2 and RDS in the same AZ for read-heavy workloads
Example — EC2 to S3 through NAT Gateway:
Private EC2 → NAT Gateway → S3
NAT Gateway charges $0.045 per GB processed
Fix: S3 Gateway Endpoint is free, eliminates NAT charges for S3 entirely
Tip

The biggest unexpected AWS networking bill is usually NAT Gateway processing fees for S3 and DynamoDB traffic. Gateway Endpoints for both services are free to create and eliminate that cost entirely. Create them on day one in every VPC.

Hands-on Lab — Build a VPC from Scratch in the Console

This lab creates a production-style VPC with public and private subnets, NAT Gateway, and an S3 VPC Endpoint — entirely through the console.

Step 1 — Create the VPC

◈ DIAGRAM
VPC → Your VPCs → Create VPC
Name: devops-prod-vpc
IPv4 CIDR: 10.0.0.0/16
Click: Create VPC
After creation:
VPC → Your VPCs → devops-prod-vpc → Actions → Edit VPC Settings
Enable: DNS hostnames ✓

Step 2 — Create 4 subnets

◈ DIAGRAM
VPC → Subnets → Create Subnet → select devops-prod-vpc
Create all 4:
Name: public-1a CIDR: 10.0.1.0/24 AZ: ap-south-1a
Name: public-1b CIDR: 10.0.2.0/24 AZ: ap-south-1b
Name: private-1a CIDR: 10.0.10.0/24 AZ: ap-south-1a
Name: private-1b CIDR: 10.0.11.0/24 AZ: ap-south-1b
For the two public subnets:
Select subnet → Actions → Edit subnet settings → Enable auto-assign public IPv4

Step 3 — Create and attach Internet Gateway

◈ DIAGRAM
VPC → Internet Gateways → Create Internet Gateway
Name: devops-igw → Create
Select devops-igw → Actions → Attach to VPC → select devops-prod-vpc

Step 4 — Create public Route Table

◈ DIAGRAM
VPC → Route Tables → Create Route Table
Name: public-rt VPC: devops-prod-vpc
Select public-rt → Routes → Edit routes → Add route:
Destination: 0.0.0.0/0 Target: devops-igw → Save
Select public-rt → Subnet Associations → Edit → check public-1a and public-1b

Step 5 — Create NAT Gateways (one per AZ)

◈ DIAGRAM
VPC → NAT Gateways → Create NAT Gateway
Name: nat-gw-1a Subnet: public-1a Elastic IP: Allocate Elastic IP → Create
Repeat for AZ-1b:
Name: nat-gw-1b Subnet: public-1b Elastic IP: Allocate Elastic IP → Create
Wait for both to show Status: Available (takes 1-2 minutes)

Step 6 — Create private Route Tables

◈ DIAGRAM
VPC → Route Tables → Create Route Table
Name: private-rt-1a VPC: devops-prod-vpc
Select private-rt-1a → Routes → Add route:
Destination: 0.0.0.0/0 Target: nat-gw-1a → Save
Subnet Associations → Edit → check private-1a
Repeat for AZ-1b:
Name: private-rt-1b, 0.0.0.0/0 → nat-gw-1b, associate with private-1b

Step 7 — Create free S3 VPC Endpoint

◈ DIAGRAM
VPC → Endpoints → Create Endpoint
Service category: AWS services
Search: s3 → select com.amazonaws.ap-south-1.s3 → Type: Gateway
VPC: devops-prod-vpc
Route Tables: select private-rt-1a and private-rt-1b → Create
AWS automatically adds a route for S3 in both private Route Tables.
S3 traffic from private subnets now goes directly — no NAT Gateway needed.

Step 8 — Enable VPC Flow Logs

◈ DIAGRAM
VPC → Your VPCs → devops-prod-vpc → Flow Logs → Create Flow Log
Filter: All
Destination: Send to CloudWatch Logs
Log group: /vpc/devops-prod-vpc → Create

Cleanup order (order matters)

◈ DIAGRAM
Delete NAT Gateways → wait until deleted (2-3 min)
Release Elastic IPs
Delete VPC Endpoints
Detach and delete Internet Gateway
Delete subnets → delete Route Tables → delete VPC

Common Mistakes to Avoid

Common Mistake

Putting EC2 instances in a public subnet when they should be in private. A public subnet means your instance has a public IP and is directly reachable from the internet. Your app servers and databases should always be in private subnets behind a Load Balancer.

Common Mistake

One NAT Gateway for the whole VPC. If that NAT Gateway's AZ has a problem, all private instances in all other AZs lose internet access. One NAT Gateway per AZ is the correct production setup. Yes it costs more. An outage costs more.

Common Mistake

Using NAT Gateway to reach S3 from private EC2. NAT Gateway charges per GB processed. The S3 Gateway Endpoint is completely free. This is one of the most common unnecessary AWS costs and takes 2 minutes to fix.

Tip

The quickest way to debug a network problem is to look at the error type. Connection timeout means something is blocking the packet — check Security Groups and NACLs. Connection refused means the packet reached the server but nothing is listening on that port — the Security Group is fine, the problem is the application. These two errors tell you exactly where to look.

Tip

The AWS VPC wizard can create a standard public-private VPC setup for you automatically. For production, always build manually so you understand exactly what was created and why. Understanding the VPC is not optional — every networking issue you ever debug traces back to it.

Resources

AWS Direct Connect vs Site-to-Site VPN Failover

AWS Direct Connect vs Site-to-Site VPN Failover

Direct Connect vs VPN isn't really either/or for production — it's a primary-plus-failover pattern. Here's how to design it, and when either/or is right.

5 min read•Aug 2026
Lambda vs Fargate vs EC2 Spot: The Cost Crossover

Lambda vs Fargate vs EC2 Spot: The Cost Crossover

Lambda vs Fargate vs EC2 Spot, at the crossover where Lambda stops being cheaper — 2026 pricing, invocation thresholds, and interruption math.

5 min read•Aug 2026
Secrets Manager vs Parameter Store vs Vault

Secrets Manager vs Parameter Store vs Vault

AWS Secrets Manager, Parameter Store, and HashiCorp Vault compared for 2026 - cost math, rotation, multi-cloud fit, and the Vault-to-OpenBao fork.

5 min read•Aug 2026
AWS VPC Security: Hardening Every Layer

AWS VPC Security: Hardening Every Layer

Most cloud security incidents start with a misconfigured VPC. Here's how to harden every layer — subnets, Security Groups, NACLs, and IAM — for production.

5 min read•Jul 2026
Event-Driven Architecture on AWS Explained

Event-Driven Architecture on AWS Explained

Event-driven architecture on AWS decouples services and absorbs traffic spikes using SQS, SNS, EventBridge, and Lambda — workflows that scale themselves.

5 min read•Jul 2026
S3 vs RDS vs DynamoDB: Choosing AWS Storage

S3 vs RDS vs DynamoDB: Choosing AWS Storage

Choosing S3, RDS, or DynamoDB wrong costs you in performance, cost, and scalability. Here is a practical decision guide based on your actual access patterns.

5 min read•Jul 2026
AWS Cost Optimisation: Cut Cloud Bills 40-60%

AWS Cost Optimisation: Cut Cloud Bills 40-60%

AWS bills surprise teams every month. Here are the 8 concrete actions that cut cloud spend by 40-60% without touching your application architecture.

5 min read•Jul 2026
EC2 vs Lambda vs Fargate: Choosing AWS Compute

EC2 vs Lambda vs Fargate: Choosing AWS Compute

EC2, Lambda, or Fargate — choosing the wrong AWS compute option costs you money and performance. Here is exactly when to use each one in production.

5 min read•Jul 2026

Explore More in AWS Networking and Security

All 6 Topics

Frequently Asked Questions

Is Amazon VPC - Subnets, Route Tables, Security Groups, and NAT Gateways free to learn on DevOps Network?

Yes - this topic, like everything on DevOps Network, is 100% free with no paywall or sign-up gate.

What does the Amazon VPC - Subnets, Route Tables, Security Groups, and NAT Gateways topic cover?

Design and build a production VPC with public and private subnets, Internet Gateway, NAT Gateway, Security Groups, and VPC Flow Logs from scratch.