Filter concepts by levelShowing all levels.

AWS · Section 7

AWS Networking — Core Model

Level
intermediate
Read
26 min
Concepts
4

A VPC is an isolated virtual network scoped to one Region — but a freshly created one is just an IP range with nothing inside it. This section covers what a VPC actually is, the fixed vocabulary of pieces that get added on top (subnets, route tables, gateways, endpoints), why "public" and "private" describe a subnet's routing rather than a setting or a name, and why planning non-overlapping CIDR ranges up front avoids a disruptive re-address later.

What is true here

  1. A VPC is isolated by default and scoped to one Region — a custom VPC starts with only its IP range, none of the connectivity pieces.
  2. Subnets, route tables, gateways, security groups, NACLs, and VPC endpoints are a fixed, composable vocabulary — each does exactly one job.
  3. A subnet is public only because its route table sends default traffic to an internet gateway — never because of its name or a setting.
  4. Non-overlapping CIDR ranges cost nothing to plan up front, but overlap blocks VPC peering or Transit Gateway connections later.

What you will be able to do

  • Explain why a newly created custom VPC has no connectivity until pieces are explicitly added
  • Name what each core VPC component (subnet, route table, gateway, endpoint) actually controls
  • Determine whether a subnet is public or private by reading its route table, not its name
  • Justify planning non-overlapping CIDR ranges across VPCs before peering is ever needed
From an empty CIDR range to a working network
builtup withroute tabledecidesplanned soit can later

VPC created

just an isolated CIDR range

Subnets, route tables, gateways added

Routing decides public vs private

Planned to stay connectable

non-overlapping CIDR from the start

  • VPC created — just an isolated CIDR range
    • leads to Subnets, route tables, gateways added (built up with)
  • Subnets, route tables, gateways added
    • leads to Routing decides public vs private (route table decides)
  • Routing decides public vs private
    • leads to Planned to stay connectable (planned so it can later)
  • Planned to stay connectable — non-overlapping CIDR from the start

Networking Core Model

What a VPC is, the pieces it is built from, what actually makes a subnet public, and planning CIDR ranges that can later connect.

A VPC is your own isolated virtual network

coreintermediate

A VPC is a private network you define inside AWS, isolated from every other customer's VPC by default. You choose its IP range, then everything else — subnets, routing, gateways — is built inside that boundary.

Think of it as

Your own fenced plot of land inside a shared city — you decide the streets (subnets) and gates (gateways) within the fence, but the fence itself is what keeps your plot separate from the neighbors'.

bash
aws ec2 create-vpc --cidr-block 10.0.0.0/16

What we're doing: See the difference between a default VPC (ready to use) and a newly created custom VPC (empty boundary).

terminalbash
aws ec2 describe-vpcs --filters Name=is-default,Values=true \
  --query "Vpcs[].{Id:VpcId,Cidr:CidrBlock}"

aws ec2 create-vpc --cidr-block 10.0.0.0/16 --query "Vpc.{Id:VpcId,Cidr:CidrBlock}"
1
The default VPC already has subnets, an internet gateway, and DNS settings ready — an instance launched into it can reach the internet immediately.
4
A freshly created custom VPC has only its IP range — no subnets, no gateway, nothing else exists inside it yet.

Why this works: The default VPC exists so a new account can launch something immediately, but production workloads almost always use a deliberately designed custom VPC instead, built up piece by piece from an empty boundary.

Assuming a newly created VPC has any internet access at all

Wrong

text
# "I created a VPC and launched an instance — why can't it reach the internet?"

Better

text
# A custom VPC needs an internet gateway attached, a route to it, and a
# public IP on the instance — none of that exists until you add it

What you see: An instance in a brand-new custom VPC has no outbound connectivity at all, unlike one launched into the account's default VPC.

Why: A custom VPC starts as just an isolated IP range — every piece of connectivity (subnets, gateways, routes) that the default VPC comes pre-wired with has to be added explicitly.

What lives inside a VPC boundary

VPC — 10.0.0.0/16

Public subnet

has a path to the internet

Private subnet

no direct internet path

Route table

decides where traffic goes

  • VPC — 10.0.0.0/16
    • Public subnet — has a path to the internet
    • Private subnet — no direct internet path
    • Route table — decides where traffic goes

Remember: A VPC is an isolated IP range you control end to end — a default VPC comes pre-wired for internet access, a custom VPC starts as an empty boundary you build up piece by piece.

See also: public vs private subnets · route tables and egress paths

VPC vocabulary: the pieces that make up a network

coreintermediate

A VPC is built from a small, fixed set of pieces that each do one job: subnets carve up the IP range, route tables decide where traffic goes, gateways connect to the outside, and security groups/NACLs filter what is allowed through.

Think of it as

A city map: districts (subnets) divide the territory, roads and signs (route tables) say how to get anywhere, checkpoints at the city gates (gateways) control what crosses in or out, and guards at each building and district (security groups, NACLs) filter who gets through.

text
VPC (CIDR) → Subnets (per-AZ slices) → Route tables → Gateways / Endpoints
                                       ↘ Security groups + NACLs (filtering)

What we're doing: See how the pieces compose: one subnet, one route table association, one gateway route.

wire-a-public-subnet.shbash
aws ec2 create-subnet --vpc-id vpc-0abc123 --cidr-block 10.0.1.0/24 \
  --availability-zone eu-west-1a

aws ec2 create-route --route-table-id rtb-0def456 \
  --destination-cidr-block 0.0.0.0/0 --gateway-id igw-0ghi789

aws ec2 associate-route-table --route-table-id rtb-0def456 --subnet-id subnet-0jkl012
1
The subnet claims a slice of the VPC's CIDR range, pinned to one specific AZ.
4
The route table gets a rule sending anything not otherwise matched (0.0.0.0/0) to the internet gateway.
7
Associating the route table with the subnet is what actually makes that subnet public — without this step the subnet has no route to the gateway at all.

Why this works: None of these three commands alone makes a subnet public — a subnet is public only once all three pieces (the subnet, a route to an internet gateway, and the association between them) are in place together.

One subnet, wired public: three pieces working together

Subnet

10.0.1.0/24

eu-west-1a

Route table

0.0.0.0/0 → igw

default route

Association

makes the subnet public

all three pieces required together

  • Subnet
    • 10.0.1.0/24 — eu-west-1a
  • Route table
    • 0.0.0.0/0 → igw — default route
  • Association
    • makes the subnet public — all three pieces required together

Confusing "has an internet gateway attached to the VPC" with "this subnet can reach the internet"

Wrong

text
# "The VPC has an internet gateway, so every subnet in it has internet access."

Better

text
# Internet access depends on that SPECIFIC subnet's route table having
# a route to the gateway — attaching the gateway to the VPC is not enough

What you see: An instance in one subnet can reach the internet while an instance in another subnet of the same VPC cannot, with no visible difference between the two instances.

Why: An internet gateway is attached to the VPC as a whole, but each subnet's own route table decides whether that subnet actually routes traffic to it — a subnet without that specific route stays private regardless of what else exists in the VPC.

The VPC component vocabulary and what each one controls

The VPC component vocabulary and what each one controls
ComponentControls
CIDR blockThe IP address range owned by the VPC or a subnet
SubnetA slice of that range, scoped to one Availability Zone
Route tableWhere traffic from a subnet is sent next
Internet gatewayA path between the VPC and the public internet
NAT gatewayOutbound-only internet access for private-subnet resources
Security groupStateful, instance-level allow rules
Network ACLStateless, subnet-level allow/deny rules
VPC endpointA private path to an AWS service, no internet gateway needed
DHCP option setDNS servers and domain name handed to instances at boot

Together

bash
aws ec2 create-subnet --vpc-id vpc-0abc123 --cidr-block 10.0.1.0/24 --availability-zone eu-west-1a

Remember: Subnets carve up the CIDR range, route tables decide where traffic goes, gateways connect outward, and security groups/NACLs filter what is let through — each piece does exactly one job.

See also: vpc as isolated network · security groups as stateful firewalls

What makes a subnet public vs private

coreintermediate

"Public" and "private" are not a subnet setting — they describe whether that subnet's route table sends traffic to an internet gateway. A subnet with that route is public; one without it is private, regardless of what it is named.

Think of it as

A door is only "the exit" because of where the hallway (the route) leads, not because of a label on the door. Rename the door "Private" and it still leads outside if the hallway still connects to the street.

text
Public  = route table has 0.0.0.0/0 → internet gateway
Private = no such route (or routes outbound only, via NAT gateway)

What we're doing: Confirm whether a subnet is public by reading its route table directly, rather than trusting its name.

terminalbash
aws ec2 describe-route-tables \
  --filters Name=association.subnet-id,Values=subnet-0jkl012 \
  --query "RouteTables[].Routes[].{Dest:DestinationCidrBlock,Target:GatewayId}"
1
This asks for the specific route table associated with one subnet — not the VPC's route tables in general.
3
A row with Dest 0.0.0.0/0 and Target starting igw- confirms the subnet is public; a nat- target confirms private with outbound-only access.

Why this works: A subnet named "subnet-private-1" with a misconfigured route table is public in every way that matters — the name is documentation, the route table is the actual behavior.

Trusting a subnet's name instead of its route table

Wrong

text
# "It's called subnet-private-app-1, so it must not be internet-reachable."

Better

text
# Read the associated route table's actual routes — the name is a label,
# the route table is the behavior

What you see: A database subnet named "private" turns out to have a stray route to an internet gateway added during earlier troubleshooting and never removed.

Why: AWS has no enforced concept of a "private" subnet — it is entirely a description of what the route table happens to contain. A name can drift out of sync with reality the moment someone edits a route without renaming the subnet.

Same VPC, two different route tables

Public subnet

  • +Route table sends 0.0.0.0/0 to the internet gateway
  • +Instances with a public IP are reachable from the internet
  • +Typical home for load balancers and NAT gateways

Private subnet

  • No route to an internet gateway
  • Outbound-only internet via a NAT gateway, if configured
  • Typical home for application servers and databases
  • Public subnet
    • Route table sends 0.0.0.0/0 to the internet gateway
    • Instances with a public IP are reachable from the internet
    • Typical home for load balancers and NAT gateways
  • Private subnet
    • No route to an internet gateway
    • Outbound-only internet via a NAT gateway, if configured
    • Typical home for application servers and databases

What decides public vs private

What decides public vs private
Subnet has...Result
A route to an internet gateway (0.0.0.0/0 → igw-...)Public — bidirectional internet reachable, with a public IP
A route to a NAT gateway (0.0.0.0/0 → nat-...)Private — outbound internet only, no inbound path
No default route at allPrivate — no internet path in either direction

Together

bash
aws ec2 describe-route-tables --route-table-ids rtb-0def456 \
  --query "RouteTables[].Routes[?DestinationCidrBlock=='0.0.0.0/0']"

Remember: Public vs private is decided entirely by the subnet's route table — a route to an internet gateway makes it public, no such route (or a NAT gateway route) makes it private, regardless of its name.

See also: vpc vocabulary · route tables and egress paths

Designing non-overlapping CIDR ranges

standardintermediate

Two VPCs with overlapping IP ranges cannot be connected later (via peering or a Transit Gateway) without re-addressing one of them — planning distinct ranges up front avoids a much more painful fix afterward.

Think of it as

Two towns that both numbered their houses 1 through 100 independently — merging the postal systems later means renumbering one town's houses, disrupting everyone who already has that address memorized.

text
prod:    10.0.0.0/16
staging: 10.1.0.0/16
dev:     10.2.0.0/16   # distinct /16s, safe to peer any pair later

What we're doing: Compare a CIDR plan that allows future peering against one that silently blocks it.

cidr-plan.txttext
# Plan A — distinct ranges, peerable later
prod-vpc:    10.0.0.0/16
staging-vpc: 10.1.0.0/16

# Plan B — both teams picked the "obvious" default independently
team-a-vpc:  10.0.0.0/16
team-b-vpc:  10.0.0.0/16   # identical to team-a — cannot be peered as-is
2
prod and staging can be peered directly whenever needed — their ranges never collide.
3
No coordination was needed between these two teams because the ranges were planned together from one shared allocation.
6
Both teams independently picked the same common default range.
7
This VPC cannot be peered with team-a's without re-addressing one of them — a disruptive change with production resources already using those IPs.

Why this works: The cost of overlap is deferred, not avoided — it surfaces only when someone actually tries to connect the two networks, often much later and under time pressure, when re-addressing is far more expensive than it would have been at design time.

Letting every team default to the same "obvious" CIDR range

Wrong

text
# Every new VPC starts from 10.0.0.0/16 because that's what the first tutorial used

Better

text
# Maintain one shared CIDR allocation plan across teams/accounts,
# so each new VPC is assigned a range that is known not to collide

What you see: A cross-account peering request fails validation months after both VPCs were already built out with real resources.

Why: CIDR planning has to happen before the range is committed to, because changing a VPC's IP range after resources are attached to it is a significantly larger project than choosing a different starting range would have been.

Remember: Overlapping CIDR ranges only become a problem once two networks need to connect — plan distinct ranges from one shared allocation up front, because re-addressing later is far more disruptive.

See also: vpc as isolated network · subnet cidr planning

Advertisement