Public vs. Private Subnets in AWS VPC: What Actually Makes the Difference

In nearly every AWS architecture diagram, you'll see resources split across "public" and "private" subnets — but AWS does not label subnets with these terms natively. The distinction is entirely determined by routing configuration, and misunderstanding it is one of the most common causes of accidental internet exposure of databases and internal services.

TL;DR

Attribute Public Subnet Private Subnet
Route to Internet Via Internet Gateway (IGW) No direct route to IGW
Outbound internet access Direct via IGW Optional, via NAT Gateway in a public subnet
Inbound from internet Possible (if Security Group allows) Not possible directly
Public IP assignment Typically auto-assigned or Elastic IP Private IP only (no public IP needed)
Typical workloads Load balancers, bastion hosts, NAT Gateways RDS, application servers, Lambda in VPC, ElastiCache

What Actually Defines a Public vs. Private Subnet

A VPC subnet is considered public when its associated route table contains a route that directs internet-bound traffic (0.0.0.0/0 for IPv4) to an Internet Gateway (IGW). That single routing entry is the definitive criterion — nothing else.

A subnet is considered private when its route table has no such route to an IGW. Resources in a private subnet cannot be reached from the internet, and they cannot initiate outbound connections to the internet unless a NAT Gateway (or NAT instance) is deployed in a public subnet and the private subnet's route table points 0.0.0.0/0 to that NAT Gateway.

Analogy: Think of your VPC as a corporate office building. The public subnet is the lobby — it has a door to the street (the IGW) that anyone can approach. The private subnet is the server room deep inside the building — no external door, accessible only through internal corridors. A NAT Gateway is like a receptionist in the lobby who can make outbound calls on behalf of the server room, but never gives out the server room's direct phone number.

Architecture: How Traffic Flows

graph TD Internet([Internet]) IGW[Internet Gateway] subgraph VPC subgraph PublicSubnet[Public Subnet] ALB[Application Load Balancer] NATGW[NAT Gateway] end subgraph PrivateSubnet[Private Subnet] AppServer[App Server] RDS[(RDS Database)] end end Internet -->|Inbound| IGW IGW --> ALB ALB -->|Internal routing| AppServer AppServer -->|DB query| RDS AppServer -->|Outbound via NAT| NATGW NATGW -->|Exits via IGW| IGW IGW --> Internet
  1. Internet → Public Subnet: Inbound traffic from the internet reaches the IGW, which routes it to resources in the public subnet (e.g., an Application Load Balancer) — subject to Security Group and Network ACL rules.
  2. Public Subnet → Private Subnet: The ALB forwards requests to application servers in the private subnet over internal VPC routing. No internet traversal occurs here.
  3. Private Subnet → Internet (outbound only): If an app server needs to reach the internet (e.g., to download a package), its traffic routes to the NAT Gateway in the public subnet, which then exits via the IGW. The source IP seen externally is the NAT Gateway's Elastic IP — the private instance's IP is never exposed.
  4. App Server → RDS: The RDS instance sits in a private subnet with no route to the IGW. It is reachable only from within the VPC (or via VPN/Direct Connect), never directly from the internet.

The Routing Tables: The Ground Truth

Below are representative route table configurations. The only difference that matters is the target for 0.0.0.0/0.

🔽 Public Subnet Route Table
Destination      Target
10.0.0.0/16      local
0.0.0.0/0        igw-0abc1234def567890   ← Internet Gateway
  
🔽 Private Subnet Route Table (with NAT for outbound)
Destination      Target
10.0.0.0/16      local
0.0.0.0/0        nat-0xyz9876fed543210   ← NAT Gateway (outbound only)
  
🔽 Private Subnet Route Table (fully isolated, no outbound internet)
Destination      Target
10.0.0.0/16      local
  

Auto-Assign Public IP: A Related but Separate Setting

Each subnet has an auto-assign public IPv4 address setting. Even if this is enabled, a resource in that subnet still cannot reach the internet unless the subnet's route table has a route to an IGW. Conversely, disabling auto-assign public IP on a subnet that has an IGW route does not make it private — it just means instances won't get a public IP automatically (you could still assign an Elastic IP manually). The route table is the authoritative control; the auto-assign setting is secondary.

Where Should Your RDS Instance Live?

Your RDS instance should always be placed in a private subnet. Here is why:

  • RDS does not need to accept inbound connections from the internet. Your application tier (in a private subnet) connects to it over the internal VPC network.
  • Placing RDS in a public subnet — even with a restrictive Security Group — unnecessarily exposes the endpoint to internet-level scanning and attack surface.
  • AWS best practice and the Well-Architected Framework's Security Pillar explicitly recommend placing data stores in private subnets.
  • For multi-AZ RDS deployments, create private subnets in at least two Availability Zones and associate them with an RDS DB Subnet Group. RDS requires a DB Subnet Group that spans multiple AZs to support Multi-AZ and read replica placement.

Security Groups vs. Network ACLs: Defense in Depth

Subnet placement (routing) is your first layer of defense. Security Groups and Network ACLs are additional layers — but they are not substitutes for correct subnet placement.

Control Scope Stateful? Default behavior
Security Group Resource (ENI) level Yes Deny all inbound, allow all outbound
Network ACL Subnet level No Default NACL allows all; custom NACLs deny all

A private subnet with a permissive Security Group is still far safer than a public subnet with a restrictive Security Group, because the routing layer prevents internet-originated packets from ever reaching the subnet in the first place.

Practical Checklist: Verifying Subnet Type

  1. Open the VPC Console → Subnets and select the subnet.
  2. Click the Route table tab.
  3. Check whether any route has 0.0.0.0/0 pointing to a target beginning with igw-. If yes → public subnet. If no → private subnet.
  4. For RDS: confirm the DB Subnet Group uses only subnets with no IGW route.

Glossary

Term Definition
Internet Gateway (IGW) A horizontally scaled, redundant VPC component that enables communication between a VPC and the internet. Must be attached to the VPC and referenced in a route table to be effective.
NAT Gateway A managed AWS service deployed in a public subnet that allows instances in private subnets to initiate outbound internet connections while preventing inbound connections from the internet.
Route Table A set of rules (routes) that determine where network traffic from a subnet or gateway is directed. Each subnet must be associated with exactly one route table.
DB Subnet Group An RDS construct that specifies a collection of subnets (typically private, in multiple AZs) within a VPC where RDS instances can be placed.
Network ACL (NACL) A stateless, subnet-level firewall that evaluates inbound and outbound traffic independently. Rules are processed in order by rule number.

Next Steps

Related Posts

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Fix Internet Gateway and Route Table

EC2 SSH Connection Timeout: Which Security Group Rules to Check

Difference Between IAM User and IAM Role: Which One Should Your EC2 Use?