AWS Subnets Explained: Public vs Private vs Isolated

A subnet is a slice of your VPC’s IP address space, locked to a single Availability Zone. The thing that trips up most beginners: subnets are not born “public” or “private.” That classification comes from one thing only: whether the subnet’s route table points to an Internet Gateway.

A quick frame before you read on:

In a typical production app, your load balancer lives in a public subnet, your app servers in a private subnet, and your database in an isolated subnet. Everything connects internally, but only the load balancer is reachable from the internet.

Simple explanation

Subnets sound technical, but the core idea is straightforward: they are just named chunks of IP address space inside your VPC, each pinned to one Availability Zone. You cannot launch a resource directly into a VPC. You always launch into a subnet.

Whether that subnet is “public” or “private” is not a fixed property. It is entirely determined by the route table attached to it. Change the route table, and the subnet changes type instantly.

🏢

Building analogy: A VPC is a private office building. You divide it into rooms (subnets), each on one floor (one Availability Zone). Some rooms have a window to the outside world so traffic can come and go freely (public). Some rooms have a side door that lets employees go out but does not let strangers walk in (private with NAT). Some rooms are completely sealed (isolated). The window or door is the route table. Swap it out, and the room changes type.

How subnets work in AWS

When you create a VPC, you define a CIDR block like 10.0.0.0/16. That is your total IP address pool: 65,536 addresses. You carve that pool into subnets by picking a smaller prefix length, such as 10.0.1.0/24 for 256 addresses.

You pick which Availability Zone the subnet lives in, and it stays there permanently. A subnet cannot span AZs. This is by design: AZ isolation means a hardware failure in one AZ does not affect subnets in others, which is exactly what makes multi-AZ architectures resilient.

Every subnet must be associated with a route table. The route table is the core control: it tells traffic where to go. Every VPC automatically includes a local route (10.0.0.0/16 local) so all subnets can reach each other. Beyond that, you decide what to add. An Internet Gateway route makes a subnet public. A NAT Gateway route gives private subnets outbound access. Nothing extra means the subnet is isolated.

To get multi-AZ redundancy, create one subnet per AZ per tier. A three-AZ, three-tier design has nine subnets total: three public, three private, three isolated.

Public vs private vs isolated subnets

Subnet typeInternet routeInbound from internetOutbound internetCommon resourcesKey notes
Public0.0.0.0/0 to IGWYes (resource needs a public IP)Yes, directlyALB, NAT Gateway, bastion hostResource also needs a public IP and an open security group to be reachable
PrivateNone to IGWNoYes, via NAT GatewayApp servers, ECS/EKS workers, Lambda ENIsNAT Gateway must sit in a public subnet; billed per GB processed
IsolatedNoneNoNoRDS, ElastiCache, secrets storesUse VPC endpoints to reach AWS services without any internet path

What makes a subnet public?

This is the most misunderstood concept in AWS networking.

Common misconception

A subnet does not become public because you enabled “auto-assign public IPv4 addresses.” That setting is about the resources you launch inside the subnet, not the subnet itself. A subnet with auto-assign on but no Internet Gateway route is still private, and those auto-assigned IPs are unreachable from the internet.

The only thing that makes a subnet public is its route table. If the route table associated with a subnet has a route for 0.0.0.0/0 pointing to an Internet Gateway, the subnet is public. There is no other flag, no creation-time setting, no property baked into the subnet itself.

So what does auto-assign public IPs actually do? It ensures that EC2 instances launched in a correctly routed public subnet automatically get a routable address. Without a public IP on the instance, even a properly configured public subnet cannot receive inbound internet traffic for that resource. It is a convenience feature, not a classification feature.

The full checklist for an internet-reachable resource
  1. Subnet route table has 0.0.0.0/0 pointing to an Internet Gateway (this makes the subnet public)
  2. The resource has a public IP, either an Elastic IP or auto-assigned (makes the resource addressable)
  3. The security group allows inbound traffic on the relevant port (permits the connection)

Only item 1 defines the subnet type. Items 2 and 3 are resource-level settings.

Even in a public subnet with a public IP, a resource is only reachable if its security group or Network ACL allows the traffic. Routing says where traffic can go. Security rules say what traffic is allowed. Both must be correct.

IPv6 and dual-stack

In a dual-stack VPC, you can add an IPv6 route (::/0 to IGW) to a subnet. IPv6 addresses are globally routable by default, and there is no NAT for IPv6. Instances receive their IPv6 address directly. This changes how you think about public exposure, so review security group rules carefully on any dual-stack subnet.

When to use each subnet type

Public subnets: place here

  • Application Load Balancers (your internet-facing entry point)
  • NAT Gateways: must be in a public subnet to give private subnets outbound access
  • Bastion hosts or jump boxes, if you use them instead of AWS Systems Manager
  • Network Load Balancers for internet-facing traffic

Private subnets: place here

  • EC2 application and API servers
  • ECS tasks and EKS worker nodes
  • Lambda functions that need VPC access (for example, to reach a private RDS or Redis)
  • Internal microservices that should not be directly reachable from the internet

Isolated subnets: place here

  • RDS databases
  • ElastiCache clusters
  • Any workload where outbound internet access is a compliance violation
  • Secrets stores, internal certificate authorities, or HSMs

How it works in a real architecture

Here is a standard three-tier web app and the path a user request takes:

User (internet)

[Public Subnet]   Application Load Balancer  ← has public IP, IGW route

[Private Subnet]  App Server (EC2 / ECS)     ← private IP only, no inbound from internet

[Isolated Subnet] RDS Database               ← no internet path in either direction

Inbound path: The user hits the ALB’s public DNS. The ALB sits in a public subnet, has a public IP, and its security group allows inbound HTTPS. It forwards the request to your app tier in a private subnet over the VPC’s internal network.

Outbound patching path: Your app server needs to pull a software update. It sends traffic to the NAT Gateway in the public subnet, which makes the outbound connection using its Elastic IP. Your app server’s private IP is never exposed to the internet.

Database access: The RDS instance in the isolated subnet is not reachable from the internet at any layer. The app server reaches it over the VPC’s local route. If RDS needs to call an AWS service like Secrets Manager, you add a VPC endpoint so that traffic stays on the AWS private backbone and never touches the internet.

This is why the three-tier layout is the default production pattern: it gives you defense in depth without unnecessary complexity. See Default vs Custom VPCs to understand why you should build this yourself rather than rely on the default VPC.

Subnet sizing and CIDR planning

AWS reserves 5 IP addresses in every subnet: the network address, the VPC router, the DNS server, a future-use address, and the broadcast address. A /24 subnet gives you 256 total, so 251 are actually usable. Plan around this from the start.

Subnet CIDRTotal IPsUsable IPsGood for
/281611VPC endpoints, NAT Gateways, small purpose-built subnets
/24256251Most workloads, a comfortable default
/23512507Large EKS node groups, busy ECS clusters
/221,0241,019High-density container clusters with many pods per node
EKS and ECS: size up or regret it

Container platforms assign one IP to every pod or task from the subnet’s pool, not just to each node. A busy EKS cluster can exhaust a /24 subnet (251 IPs) faster than you expect once you factor in pods per node. Use /23 or larger for any subnet that will hold container workloads, and commit to that CIDR before you deploy. Resizing later is painful.

A typical three-AZ, three-tier design needs nine subnets. With a /16 VPC and /24 subnets, you use 9 of your 256 available blocks, leaving plenty of room to grow.

VPC: 10.0.0.0/16

Public subnets (ALB, NAT GW):
  10.0.1.0/24  →  us-east-1a
  10.0.2.0/24  →  us-east-1b
  10.0.3.0/24  →  us-east-1c

Private subnets (app servers, ECS/EKS):
  10.0.11.0/24  →  us-east-1a
  10.0.12.0/24  →  us-east-1b
  10.0.13.0/24  →  us-east-1c

Isolated subnets (RDS, ElastiCache):
  10.0.21.0/24  →  us-east-1a
  10.0.22.0/24  →  us-east-1b
  10.0.23.0/24  →  us-east-1c

Common mistakes

  1. Confusing route tables with security groups. Route tables control where packets can flow. Security groups control what traffic is allowed. When something is not reachable, check routing first, then check security group rules. See troubleshooting network issues for a structured debugging approach.
  2. Making subnets too small for containers. EKS assigns one IP per pod, not per node. A /24 with 251 usable IPs runs out fast once you have more than a handful of nodes. Size container subnets at /23 or larger from the start.
  3. Assuming “private” means no outbound internet. A private subnet with a NAT Gateway route does have outbound internet access. If you need to block outbound internet entirely, use an isolated subnet or explicitly remove the NAT Gateway route from the route table.
  4. Not tagging subnets for Kubernetes. EKS uses specific subnet tags to discover where to create load balancers and pod networking. Public subnets need kubernetes.io/role/elb: 1 and private subnets need kubernetes.io/role/internal-elb: 1. Missing these causes load balancer provisioning to fail silently.
  5. Forgetting VPC endpoints for isolated subnets. Isolated subnets have no internet path, but your workload may still need to reach S3, Secrets Manager, or other AWS services. Without VPC endpoints, those calls fail silently. Plan your endpoint strategy before isolating a workload.
  6. Accidentally enabling auto-assign public IPs on the wrong subnet. If auto-assign is on and the route table has an IGW route, every instance launched there is internet-addressable. Always verify both the route table and the auto-assign setting on any subnet that is supposed to be private.

Which subnet type should you use?

ScenarioRecommended subnet type
Internet-facing load balancerPublic
NAT GatewayPublic
Web or API serversPrivate
ECS tasks, EKS worker nodes, Lambda in VPCPrivate
RDS or Aurora databaseIsolated (preferred) or Private
ElastiCache clusterIsolated or Private
Compliance workload with no outbound internet permittedIsolated
Internal tooling reachable only inside the VPCPrivate or Isolated

When in doubt, default to private. Use isolated when a compliance requirement or risk model says outbound internet must be impossible. Only put a resource in a public subnet if it genuinely needs to accept inbound traffic from the internet.

Creating subnets with the AWS CLI

Notice that all three subnet types are created identically. The difference between public, private, and isolated is made by the route table you associate afterwards, not by anything in the create-subnet call itself.

# Create a public subnet in us-east-1a
aws ec2 create-subnet \
  --vpc-id vpc-0123456789abcdef0 \
  --cidr-block 10.0.1.0/24 \
  --availability-zone us-east-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-1a},{Key=Type,Value=public}]'

# Enable auto-assign public IP (a resource-level convenience, not what makes it "public")
aws ec2 modify-subnet-attribute \
  --subnet-id subnet-0123456789abcdef0 \
  --map-public-ip-on-launch

# Create a private subnet
aws ec2 create-subnet \
  --vpc-id vpc-0123456789abcdef0 \
  --cidr-block 10.0.11.0/24 \
  --availability-zone us-east-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-1a},{Key=Type,Value=private}]'

# Create an isolated subnet
aws ec2 create-subnet \
  --vpc-id vpc-0123456789abcdef0 \
  --cidr-block 10.0.21.0/24 \
  --availability-zone us-east-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=isolated-1a},{Key=Type,Value=isolated}]'

Frequently asked questions

Can a subnet span multiple Availability Zones?

No. Each subnet is tied to exactly one Availability Zone. To achieve multi-AZ redundancy, create multiple subnets, one per AZ, and distribute your resources across them.

What makes a subnet public?

A subnet is public when its route table has a 0.0.0.0/0 route pointing to an Internet Gateway. That is the only thing that defines it. Auto-assigning public IPs is a separate subnet setting that controls whether instances launched there get a public IP automatically. Useful, but not what makes the subnet public or private.

Does a public subnet automatically expose all instances to the internet?

No. Being in a public subnet does not make an instance reachable. The instance also needs a public IP address, and its security group must allow inbound traffic on the relevant port. Subnet type controls routing; security groups and NACLs control what is actually allowed.

What is the difference between a private subnet and an isolated subnet?

Private subnets have no inbound internet route but can reach the internet for outbound traffic via a NAT Gateway. Isolated subnets have no route to the internet in either direction, not even through a NAT Gateway. Use isolated subnets when any internet path must be eliminated entirely.

Can isolated subnets reach AWS services like S3 or Secrets Manager?

Not by default, because there is no internet path. To let resources in isolated subnets call AWS services, add VPC endpoints. These route traffic over the AWS private backbone and never touch the internet.

How many IP addresses does AWS reserve in each subnet?

AWS reserves 5 IP addresses in every subnet: the network address, the VPC router address, the DNS server address, a reserved address for future use, and the broadcast address. A /24 subnet has 256 total addresses, giving you 251 usable ones.

Last verified: 21 April 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.