Skip to content
Associate5 min read

Subnet Tiering and Route Table Scoping

A subnet is not public or private by declaration — it is defined entirely by the route table associated with it. Getting the association model right is what makes every later networking decision possible.

Published Feb 5, 2026

There is no public = true attribute on a subnet. "Public" and "private" are conventions, not properties. A subnet is public if and only if the route table associated with it has a default route to an internet gateway. Everything else — naming, tags, the CIDR you chose — is documentation.

Internalizing that one fact resolves most of the confusion around VPC design, because it makes the route table association the real unit of network topology rather than the subnet.

The evaluation order

When a packet leaves an instance, it is evaluated in a fixed order. Knowing the order tells you where to look when traffic disappears.

10.0.11.34:51204 → 93.184.216.34:443 (TCP SYN)
  1. EC2

    Instance ENI

    Source address must belong to the subnet CIDR

  2. FILTER

    Security group — egress rules

    Stateful: the return packet is allowed automatically

  3. FILTER

    Network ACL — outbound rules

    Stateless: the return path needs its own inbound rule

  4. RTB

    Route table for THIS subnet

    Most specific matching prefix wins; local always wins inside the VPC

  5. GW

    Target of the matched route

    igw, nat, tgw, vpce, eni, or a peering connection

Security groups are stateful and NACLs are not. That single asymmetry explains most 'it works one way' bugs.

Two things are worth pausing on.

Security groups are stateful; NACLs are not. If you allow outbound HTTPS in a security group, the response is permitted automatically. A NACL evaluates each direction independently, so blocking inbound ephemeral ports (1024–65535) breaks every outbound connection while looking like a perfectly reasonable inbound restriction. Asymmetric connectivity — a connection that establishes in one direction but not the other — is almost always a NACL.

Route selection is longest-prefix-match, and local cannot be overridden. A route for 10.0.0.0/16 → local always wins for in-VPC destinations. You cannot force intra-VPC traffic through an inspection appliance by adding a competing route for the VPC CIDR; you need separate VPCs or subnet-level steering with more specific prefixes.

Address planning that survives contact with reality

Two rules prevent most future pain:

Do not overlap with anything you might one day connect to. Overlapping CIDRs cannot be routed between. Not with peering, not with Transit Gateway, not with VPN. Remediation means renumbering a live VPC, which is a migration, not a change. Assume every VPC will eventually need to reach on-premises and every acquisition will use 10.0.0.0/16.

Leave room in each tier. Subnets cannot be resized after creation. A /28 public subnet that seemed generous stops accepting new load balancer ENIs at the worst possible moment.

A layout that holds up for a three-AZ VPC in 10.0.0.0/16:

| Tier | Per-AZ CIDR | Usable IPs | Contents | | --- | --- | --- | --- | | Public | 10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24 | 251 | ALB/NLB ENIs, NAT Gateways | | Private app | 10.0.16.0/20, 10.0.32.0/20, 10.0.48.0/20 | 4091 | Compute, EKS pods, interface endpoints | | Private data | 10.0.128.0/24, 10.0.129.0/24, 10.0.130.0/24 | 251 | RDS subnet group, ElastiCache |

The gap between 10.0.48.0/20 and 10.0.128.0/24 is intentional. Leaving the second half of the range unallocated means a fourth AZ, a new tier, or a secondary CIDR does not force renumbering.

One route table per AZ, not one per VPC

The most common structural mistake in VPC design is a single rtb-private associated with the private subnets in all three AZs.

It looks like sensible deduplication and it forecloses two things you will want:

  • Per-AZ egress. A route table holds one route per destination prefix. One shared private route table means one 0.0.0.0/0 target, so all three AZs must egress through the same NAT Gateway — a zonal single point of failure plus cross-AZ data processing charges on two thirds of your traffic.
  • Zonal failure isolation. Per-AZ route tables let you steer a single AZ's traffic elsewhere during an impairment. A shared table forces an all-or-nothing change.
routing.tf+9-3
Diff of routing.tf: 9 lines added, 3 lines removed.
1-# One shared private route table for every AZ
1+# One private route table per AZ, each pointing at that AZ's NAT Gateway.
2+# The route table is the unit of association, so per-AZ egress requires
3+# per-AZ tables — there is no way to express it in a shared one.
24 resource "aws_route_table" "private" {
5+for_each = aws_subnet.private
6+ 
37 vpc_id = aws_vpc.main.id
48  
59 route {
610 cidr_block = "0.0.0.0/0"
7- nat_gateway_id = aws_nat_gateway.main.id
11+ nat_gateway_id = aws_nat_gateway.per_az[each.key].id
812 }
13+ 
14+tags = { Name = "rtb-private-${each.key}" }
915 }
1016  
1117 resource "aws_route_table_association" "private" {
1218 for_each = aws_subnet.private
1319  
1420 subnet_id = each.value.id
15-route_table_id = aws_route_table.private.id
21+route_table_id = aws_route_table.private[each.key].id
1622 }

Keying route tables by the same map used for subnets keeps the association one-to-one and makes the per-AZ intent explicit in the plan output.

A checklist that actually finds problems

When connectivity fails, work the path in order rather than checking your favourite suspect first:

  1. Establish the failure mode. Timeout, refusal, TLS error, or DNS failure. This eliminates most of the list immediately.
  2. Identify the source subnet, then the route table associated with that specific subnet — not the one you assume applies.
  3. Find the matching route by longest prefix, and confirm its target exists and is not blackhole.
  4. If the target is a middlebox — NAT Gateway, firewall endpoint, Transit Gateway attachment — repeat steps 2 and 3 for its subnet. This is the step that gets skipped, and it is where Lab 01 lives.
  5. Check NACLs in both directions before security groups. Stateless rules break return paths in ways stateful rules cannot.

Next

NAT Gateway placement and egress cost applies this association model to the egress path, including what the per-gigabyte charges look like when the topology is wrong.