Filter concepts by levelShowing all levels.

AWS · Section 45

AWS Networking at Senior Level

Level
advanced
Read
35 min
Concepts
4

The senior-level question is not how a VPC works but which of the connectivity mechanisms fits: peering for two VPCs and no more, since it is non-transitive; Transit Gateway as a hub for many VPCs plus on-premises, billed per attachment-hour and per GB; gateway endpoints for S3 and DynamoDB, which are free route-table entries; PrivateLink for reaching a service rather than a network; Direct Connect or VPN for on-premises; and one centralized NAT rather than one per VPC. Assembling those into a hub-and-spoke topology gives each spoke a single route to the hub and turns segmentation into a route-table association — but it only works on top of an address plan with no overlapping CIDRs anywhere, because overlapping networks cannot be routed between at all and a VPC's primary CIDR cannot be changed afterwards. Inside each VPC the layout is three tiers across at least two AZs: public subnets defined solely by their route to an internet gateway, private subnets routing to their own AZ's NAT plus gateway endpoints, and a data tier with no default route so it cannot initiate outbound traffic. When something cannot connect, the five checks run in a fixed order — DNS, route, security groups on both sides, NACLs in both directions including ephemeral ports, then the listener — and the first one that fails is the answer.

What is true here

  1. Peering is non-transitive; Transit Gateway is the hub that provides transitive routing, at a per-attachment and per-GB cost.
  2. Gateway endpoints for S3 and DynamoDB are free and bypass NAT data-processing charges.
  3. One central, non-overlapping address plan, made before the first VPC exists — a primary CIDR cannot be changed.
  4. A subnet is public only because of its route to an internet gateway; the data tier should have no default route at all.
  5. Troubleshoot DNS → route → SG → NACL → listener; the first failing check is the cause.

What you will be able to do

  • Choose between peering, Transit Gateway, PrivateLink, endpoints, VPN and Direct Connect for a given requirement
  • Design a hub-and-spoke topology with route-table segmentation and centralized egress
  • Produce a non-overlapping address plan with room for growth and on-premises
  • Lay out multi-AZ public/private/data subnets and explain every route in every table
  • Diagnose a connectivity failure in five ordered checks without changing anything at random
From connectivity choices to a network you can debug
assembledintolaid out asdebuggedwith

The connectivity mechanisms

Hub-and-spoke + address plan

Multi-AZ subnets, route by route

DNS → route → SG → NACL → listener

  • The connectivity mechanisms
    • leads to Hub-and-spoke + address plan (assembled into)
  • Hub-and-spoke + address plan
    • leads to Multi-AZ subnets, route by route (laid out as)
  • Multi-AZ subnets, route by route
    • leads to DNS → route → SG → NACL → listener (debugged with)
  • DNS → route → SG → NACL → listener

AWS Networking at Senior Level

The full set of connectivity mechanisms, hub-and-spoke topology and address planning, multi-AZ subnet layout, and a fixed troubleshooting order.

The Full Connectivity Surface

coreadvanced

At a senior level the question stops being "how does a VPC work" and becomes "which of the eight ways to connect two networks is right here". Peering, Transit Gateway, VPN, Direct Connect, PrivateLink, VPC endpoints, NAT, and public internet each solve a different shape of problem, and each has a different cost and failure mode.

Think of it as

Two axes decide almost every choice: is this AWS-to-AWS or AWS-to-elsewhere, and does the traffic need to be private or merely secure? The answer picks the mechanism, and the remaining work is routing and address planning.

What we're doing: Decide between a peering mesh and a Transit Gateway as the VPC count grows.

mesh-vs-hub.txttext
3 VPCs, full mesh: 3 peering connections. Simple, no hourly cost.

6 VPCs, full mesh: 15 peering connections, and every VPC's route table
carries 5 peering routes. Still workable, already tedious.

12 VPCs, full mesh: 66 peering connections, 11 routes per route table,
and peering is non-transitive so on-premises access must be built
separately for each VPC.

Transit Gateway: 12 attachments, one route table, on-premises attached
once. Costs per attachment-hour and per GB — which is the trade.
1
Peering is genuinely the right answer at small scale. Reaching for a hub on day one is over-engineering.
5
The pain is quadratic in the number of route table entries, not just in the number of connections.
9
This is the tipping point in practice: the moment on-premises connectivity has to reach every VPC, the mesh stops scaling and the hub pays for itself.

Why this works: The mesh-versus-hub decision is arithmetic, not taste. Peering costs nothing hourly and scales quadratically in operational effort; Transit Gateway costs per attachment and per GB and scales linearly. Knowing where the crossover is for your estate is the senior-level part.

Expecting peering to be transitive

Wrong

text
# VPC A peers with B. B peers with C. Assume A can reach C through B.

Better

text
# Peer A with C directly, or attach all three to a Transit Gateway

What you see: Traffic from A to C is silently dropped. Both peering connections are active, both route tables look correct, and nothing reports an error.

Why: VPC peering forwards traffic only between the two peered VPCs — routes learned through a peering connection are not re-advertised onward. Transit Gateway exists precisely to provide the transitive routing peering deliberately does not.

Which mechanism for which connection

AWS to AWS

VPC peering

two VPCs, non-transitive, no hourly charge

Transit Gateway

many VPCs + on-premises, transitive

PrivateLink / endpoints

reach a service, not a network

AWS to elsewhere

Direct Connect

dedicated link, consistent latency

Site-to-Site VPN

over the internet, encrypted, BGP

NAT gateway

outbound only, to the internet

  • AWS to AWS
    • VPC peering — two VPCs, non-transitive, no hourly charge
    • Transit Gateway — many VPCs + on-premises, transitive
    • PrivateLink / endpoints — reach a service, not a network
  • AWS to elsewhere
    • Direct Connect — dedicated link, consistent latency
    • Site-to-Site VPN — over the internet, encrypted, BGP
    • NAT gateway — outbound only, to the internet

Choosing a connectivity mechanism

Choosing a connectivity mechanism
NeedMechanismWatch out for
Two VPCs talking, and only twoVPC peeringNon-transitive; a mesh of N VPCs needs N(N−1)/2 links
Many VPCs plus on-premisesTransit GatewayPer-attachment-hour and per-GB charges
Reach S3 or DynamoDB privatelyGateway VPC endpointRoute-table based and free — always worth adding
Reach another AWS service or a partner service privatelyInterface endpoint (PrivateLink)Hourly per ENI, per AZ, plus data processing
Consistent, high-bandwidth on-premises linkDirect ConnectLead time to provision; needs a VPN backup for resilience
Encrypted on-premises link todaySite-to-Site VPNInternet-path latency variability
Outbound internet from private subnetsNAT gatewayPer-GB processing — centralize it, or use endpoints

Together

text
# Centralized egress: one NAT for the whole organization
spoke VPCs --(TGW attachment)--> Transit Gateway --> egress VPC
                                                     -> NAT gateway -> IGW
# Spoke route table: 0.0.0.0/0 -> tgw-attachment
# TGW route table:   0.0.0.0/0 -> egress VPC attachment

Remember: Peering for two VPCs (non-transitive, no hourly cost); Transit Gateway for many plus on-premises (per attachment-hour, per GB); gateway endpoints for S3/DynamoDB (free, always add them); PrivateLink for private service access; Direct Connect or VPN for on-premises; centralize NAT rather than one per VPC.

See also: hub and spoke and address planning · multi az subnet architecture · vpc endpoints

Hub-and-Spoke and Non-Overlapping Address Space

coreadvanced

A hub-and-spoke network puts one Transit Gateway at the centre and attaches every VPC and every on-premises link to it. Each spoke has one route to the hub instead of one route per peer. The rule that makes this possible is boring and absolute: no two networks that might ever need to talk may use overlapping CIDR ranges.

Think of it as

Address planning is like assigning phone numbers. Two departments can each pick 555-0100 as long as they never need to call each other — and the day they do, one of them has to renumber, which is far more painful than choosing carefully at the start.

What we're doing: See what an overlapping CIDR actually costs, two years later.

overlap.txttext
2024: two teams independently create VPCs, both using 10.0.0.0/16
because it is the console default suggestion. They never talk.

2026: the company needs the two systems integrated, and both must reach
on-premises through one Direct Connect link.

Neither can be routed to the other. The only fixes are: recreate one VPC
with a different CIDR and migrate every resource into it, or put NAT in
between and lose the ability to address hosts directly.
1
Nothing is wrong at this point. Both VPCs work perfectly, and the default CIDR is a reasonable local choice.
6
A VPC's primary CIDR cannot be changed. "Recreate and migrate" means every subnet, every ENI, every hard-coded address, and a maintenance window.

Why this works: Address planning is the one networking decision that is almost impossible to undo and costs nothing to get right. Ten minutes allocating from a documented block prevents a migration project, which is why it is worth doing before the first VPC exists.

Letting each team pick its own VPC CIDR

Wrong

text
# Every account creates a VPC with whatever range the wizard suggests

Better

text
# One documented allocation table (or IPAM) hands out a non-overlapping
# /16 per VPC, including reserved space for on-premises

What you see: Half the estate is on 10.0.0.0/16, and any future connectivity between those accounts requires re-addressing rather than routing.

Why: The console default is the same for everyone, so independent teams converge on identical ranges. Central allocation is the only thing that prevents it, and it has to exist before the VPCs do — retrofitting means recreating them.

Hub and spoke, with segmentation
attachmentattachmentattachment(dev table)0.0.0.0/0DXattachment

prod-app VPC

10.20.0.0/16

prod-data VPC

10.21.0.0/16

dev VPC

10.60.0.0/16

Transit Gateway

prod route table · dev route table

egress VPC

10.10.0.0/16 · shared NAT

On-premises

172.16.0.0/12 via Direct Connect

  • prod-app VPC — 10.20.0.0/16
    • leads to Transit Gateway (attachment)
  • prod-data VPC — 10.21.0.0/16
    • leads to Transit Gateway (attachment)
  • dev VPC — 10.60.0.0/16
    • leads to Transit Gateway (attachment (dev table))
  • Transit Gateway — prod route table · dev route table
    • leads to egress VPC (0.0.0.0/0)
    • leads to On-premises (DX attachment)
  • egress VPC — 10.10.0.0/16 · shared NAT
  • On-premises — 172.16.0.0/12 via Direct Connect

An address plan that survives growth

An address plan that survives growth
BlockPurposeWhy it is reserved
10.0.0.0/12Shared services and egress VPCsSmall in count, referenced by everything
10.16.0.0/12Production workload VPCs, one /16 eachRoom for 16 production VPCs per Region
10.48.0.0/12Non-production VPCsSegregated so a route table split is trivial
10.128.0.0/9UnallocatedThe half you have not spent — keep it that way
172.16.0.0/12On-premisesNever allocate this in AWS

Together

text
# Per-VPC layout inside a /16, four AZs, room to grow
10.20.0.0/16   prod-app VPC
  10.20.0.0/20   public   AZ-a       10.20.16.0/20  public   AZ-b
  10.20.64.0/20  private  AZ-a       10.20.80.0/20  private  AZ-b
  10.20.128.0/20 data     AZ-a       10.20.144.0/20 data     AZ-b
  10.20.192.0/18 reserved for growth

Remember: Hub-and-spoke: one Transit Gateway, one route per spoke, and route-table associations for segmentation. Allocate every CIDR from one documented plan, reserve on-premises space, size generously — a VPC's primary CIDR cannot be changed, and overlapping networks cannot be routed to each other at all.

See also: the full networking surface · multi az subnet architecture · non overlapping cidr design

Multi-AZ Subnet Architecture, Route by Route

coreadvanced

A production VPC has three subnet tiers repeated across at least two Availability Zones: public for load balancers and NAT, private for compute, and isolated for databases. Each tier has its own route table, and the difference between the tiers is entirely in those routes. Being able to explain every line of every route table is the actual skill.

Think of it as

A subnet is not public or private by nature — it is public because its route table has a route to an internet gateway. Change that one line and the subnet changes category. Every access question in a VPC eventually reduces to reading route tables.

What we're doing: Understand why one NAT gateway saves money and creates a cross-AZ outage.

nat-per-az.txttext
Cost-optimized: one NAT gateway in public-a.
  private-a: 0.0.0.0/0 -> nat-a
  private-b: 0.0.0.0/0 -> nat-a      <- cross-AZ

AZ A becomes unavailable. Compute in AZ B is healthy and still running,
but has no outbound internet: it cannot pull images, reach Secrets
Manager over the internet, or call third-party APIs.

Resilient: nat-a and nat-b, each private subnet routing to its own AZ's
NAT. Roughly double the NAT hourly cost, and no cross-AZ dependency.
1
This is a legitimate choice for a non-production environment, where the saving is real and the risk is acceptable.
6
The failure is subtle: instances are up, health checks that do not leave the VPC pass, and only outbound-dependent work fails.

Why this works: Multi-AZ is not achieved by placing resources in two AZs — it is achieved by removing every cross-AZ dependency between them. A shared NAT gateway is the most common one, and it is invisible until the AZ it lives in has a problem.

Putting a database in a subnet whose route table has a default route

Wrong

text
# RDS placed in the private (NAT-routed) subnets, because "private is
# private"

Better

text
# A third tier with no 0.0.0.0/0 route at all — the database can reach
# the VPC and nothing else

What you see: A compromised database host — or a misconfigured extension, or an exfiltration attempt — has a working outbound path to the internet through NAT.

Why: NAT permits outbound connections, which is exactly what data exfiltration needs. A data tier with no default route can still be reached by the application over the `local` route and cannot initiate anything outward, which removes the path rather than filtering it.

Three tiers, two AZs

Availability Zone A

public-a 10.20.0.0/20

ALB node, NAT gateway A

private-a 10.20.64.0/20

ECS tasks, EC2

data-a 10.20.128.0/20

RDS, ElastiCache — no default route

Availability Zone B

public-b 10.20.16.0/20

ALB node, NAT gateway B

private-b 10.20.80.0/20

ECS tasks, EC2

data-b 10.20.144.0/20

RDS standby

  • Availability Zone A
    • public-a 10.20.0.0/20 — ALB node, NAT gateway A
    • private-a 10.20.64.0/20 — ECS tasks, EC2
    • data-a 10.20.128.0/20 — RDS, ElastiCache — no default route
  • Availability Zone B
    • public-b 10.20.16.0/20 — ALB node, NAT gateway B
    • private-b 10.20.80.0/20 — ECS tasks, EC2
    • data-b 10.20.144.0/20 — RDS standby

Every route, and why it is there

Every route, and why it is there
Route tableDestination → TargetWhy
public (both AZs)10.20.0.0/16 → localReach the rest of the VPC; cannot be removed
public (both AZs)0.0.0.0/0 → igw-…This route is what makes the subnet public
private-a10.20.0.0/16 → localSame as above
private-a0.0.0.0/0 → nat-aOutbound only, and via this AZ's NAT so an AZ failure is contained
private-apl-s3 → vpce-…S3 traffic bypasses NAT — no per-GB processing charge
data-a10.20.0.0/16 → localThe only route: no path to or from the internet at all

Together

text
# Read the route table before anything else when access is unexpected
aws ec2 describe-route-tables \
  --filters Name=association.subnet-id,Values=subnet-0a1b2c3d \
  --query "RouteTables[].Routes[].{dst:DestinationCidrBlock,gw:GatewayId,nat:NatGatewayId}"

Remember: Three tiers per AZ: public (default route to the IGW), private (default route to that AZ's NAT, plus gateway endpoints), data (no default route at all). Every route table starts with an unremovable `local` route. Multi-AZ means removing cross-AZ dependencies, not just placing resources in two AZs.

See also: network troubleshooting order · hub and spoke and address planning · route tables and egress paths

Troubleshooting in Order: DNS → Route → SG → NACL → Listener

coreadvanced

"It cannot connect" has five possible causes and they sit in a fixed order along the path. Resolve the name, find a route to the address, get past the source and destination security groups, get past the network ACLs, and finally reach something listening on that port. Checking them in order turns a guessing game into five short questions.

Think of it as

Each layer can only fail after the one before it succeeded, so the first failing check is the answer. Starting in the middle — the usual instinct is to blame the security group — means you cannot tell a security-group problem from a DNS problem that never got that far.

What we're doing: Diagnose "the app cannot reach the database" without changing anything at random.

five-checks.txttext
1. dig db.internal from a task in private-a
   -> 10.20.128.41. Resolves. DNS is fine.

2. Route table for private-a: 10.20.0.0/16 -> local covers 10.20.128.41.
   Route is fine.

3. sg-db inbound: 5432 from sg-task. Present. sg-task outbound: allow
   all. Fine.

4. NACL on data-a: inbound allows 5432 from 10.20.64.0/20. Outbound
   allows 5432 only — not the ephemeral range 1024-65535.
   -> The reply is dropped. This is the failure.

5. Not reached. The listener was never the problem.
1
Thirty seconds, and it eliminates the whole class of failures that look identical to a firewall block.
4
The `local` route covers everything inside the VPC and cannot be removed, so intra-VPC routing is rarely the answer — but confirming it is still faster than guessing.
10
The classic NACL failure. The inbound rule is obviously right, the outbound rule is subtly wrong, and the symptom is a timeout indistinguishable from a security-group problem.

Why this works: Each check is cheap and each one eliminates a whole class of cause. The alternative — editing the security group because that is the usual culprit — leaves the rule permanently widened and the real problem untouched.

Widening a security group to 0.0.0.0/0 to test whether it is the problem

Wrong

text
# Temporarily allow 0.0.0.0/0 on the database security group "just to
# rule it out"

Better

text
# Read the rules and the flow logs instead. If a test is genuinely
# needed, allow the one source security group, not the internet.

What you see: The temporary rule outlives the incident and shows up months later in an audit, on a database that has been reachable from the internet ever since.

Why: A widened rule during an incident is added under pressure and removed only if someone remembers. Flow logs answer the same question without changing anything, and a scoped test rule answers it without creating an exposure worth auditing.

Five checks, in order

1 · DNS

Does the name resolve, and to the address you expect? Private zone, resolver, split-horizon.

2 · Route

Does the source subnet's route table have a path to that address? Silent drop if not.

3 · Security groups

Source SG allows outbound; destination SG allows inbound from the source SG. Timeout if not.

4 · NACLs

Both directions, both subnets, including ephemeral ports on the return path.

5 · Listener

Is something bound to that port, on that interface? Connection refused if not.

  1. 1 · DNS — Does the name resolve, and to the address you expect? Private zone, resolver, split-horizon.
  2. 2 · Route — Does the source subnet's route table have a path to that address? Silent drop if not.
  3. 3 · Security groups — Source SG allows outbound; destination SG allows inbound from the source SG. Timeout if not.
  4. 4 · NACLs — Both directions, both subnets, including ephemeral ports on the return path.
  5. 5 · Listener — Is something bound to that port, on that interface? Connection refused if not.

Reading the symptom back to the layer

Reading the symptom back to the layer
SymptomMost likely layerHow to confirm
Name does not resolveDNS`dig` from inside the VPC; check the private hosted zone and resolver
Resolves, then hangs and times outRoute, SG, or NACLFlow logs: no record → route; REJECT → SG/NACL
Connection refused immediatelyListenerThe packet arrived — check the process and its bind address
Works one direction onlyNACLStateless rules: the return path needs its own allow
Works from one subnet, not anotherRoute table or NACLCompare the two subnets' associations
Intermittent failureA per-AZ asymmetryCompare route tables and NACLs per AZ, not just per VPC

Together

text
# Step 5, decided in one line: refused vs timeout
nc -vz db.internal 5432
#   "Connection refused"  -> the packet arrived; nothing is listening
#   hangs, then times out -> route, security group, or NACL

Remember: DNS → route → security groups (both sides) → NACLs (both directions, ephemeral ports) → listener. The first failing check is the answer. Refused means the packet arrived; timeout means it did not. Flow logs tell REJECT from never-arrived.

See also: multi az subnet architecture · ephemeral ports and statefulness · aws log sources and retention

Advertisement