Public vs private subnets, security groups vs Network ACLs as complementary stateful/stateless layers, the NAT Gateway's role, and the basics of VPC peering.
Published September 23, 2026
A VPC (Virtual Private Cloud) is an isolated network within AWS — this lesson covers the core building blocks that determine what can reach what, closing out the networking picture that Services & Ingress covered at the Kubernetes layer, one level below the cloud's own network.
Public subnet: has a route to an Internet Gateway — resources CAN have a public IP
and be directly reachable from the internet
Private subnet: NO route to an Internet Gateway — resources have no direct path
to/from the public internet at all
The distinction is entirely about ROUTING, not some inherent property of the subnet itself: a subnet is "public" simply because its route table sends internet-bound traffic to an Internet Gateway. The standard production pattern places genuinely internet-facing components (a load balancer, a bastion host) in public subnets, while application servers and databases sit in PRIVATE subnets — directly reachable from within the VPC, but never directly from the internet, meaningfully reducing the attack surface even before any security-group-level restriction is applied.
Security Group: INSTANCE-level, STATEFUL, ALLOW rules only
Network ACL: SUBNET-level, STATELESS, supports both ALLOW and DENY rules
This is a genuinely important complementary-layers distinction, easy to conflate: security groups (covered in EC2) apply per-instance and are stateful (a permitted inbound connection's response traffic is automatically allowed back out). Network ACLs apply at the SUBNET level (affecting every resource in that subnet uniformly) and are STATELESS — meaning both inbound AND outbound rules must be explicitly defined for a connection to work in both directions, and they DO support explicit DENY rules, unlike security groups. In practice, most workloads rely primarily on security groups for fine-grained, per-instance control, with Network ACLs used more sparingly as a coarser, subnet-wide backstop (e.g. explicitly blocking a known-bad IP range at the subnet level, something security groups' allow-only model can't directly express).
Private subnet resource → NAT Gateway (sits in a PUBLIC subnet) → Internet Gateway → internet
— outbound traffic works (e.g. downloading a package, calling an external API)
— but nothing from the internet can INITIATE a connection back to the private resource
A resource in a private subnet still often needs OUTBOUND internet access (pulling a Docker image, calling a third-party payment processor's API from Payment — Security) without being directly, inbound-reachable from the internet. A NAT Gateway (placed in a public subnet, with a route from the private subnet pointing to it) provides exactly this: outbound connectivity initiated FROM the private resource works, while the private resource remains impossible to reach via an inbound connection initiated from outside — a one-directional door, not a hole punched through the isolation.
VPC peering connects two separate VPCs (within the same or different AWS accounts) so resources in each can communicate using PRIVATE IP addresses, as if they were on the same network — without traffic ever traversing the public internet. A common use case: connecting a staging VPC's resources to a shared services VPC (holding, say, a shared artifact repository), or connecting VPCs across acquired/merged teams' AWS accounts — each peered connection is explicitly established and non-transitive (peering A↔B and B↔C does NOT automatically let A reach C; that would need its own separate peering connection, or a more centralized solution like AWS Transit Gateway for larger, many-VPC topologies).
Q: If security groups already provide instance-level firewalling, what's the practical value of also configuring Network ACLs? A: Defense in depth, and a genuinely different failure mode it protects against — a MISCONFIGURED security group (accidentally too permissive) on one instance is contained by a correctly-configured Network ACL at the subnet level; relying on security groups alone means a single misconfiguration has no second layer of protection behind it.
Q: Does a NAT Gateway introduce a single point of failure for all private-subnet outbound traffic? A: It can, if only one is deployed — the standard production pattern deploys a NAT Gateway PER AVAILABILITY ZONE (rather than one shared across all AZs), so an AZ-level failure doesn't take down outbound connectivity for private resources in the other AZs, directly paralleling the multi-AZ redundancy reasoning from RDS.
Q: Why is VPC peering non-transitive, given it seems like an inconvenient limitation? A: It's a deliberate design choice for predictability and security — automatically transitive peering would mean a new peering connection could silently open up reachability paths nobody explicitly intended (A peering with B, then B later peering with C, accidentally exposing A to C); requiring each connection to be explicit keeps the actual reachability graph fully auditable.
Q: How does the public/private subnet split interact with the Ingress/LoadBalancer concepts from Services & Ingress when running Kubernetes on AWS (EKS)?
A: Directly — an EKS cluster's worker nodes typically live in PRIVATE subnets (not directly internet-reachable), while a Kubernetes LoadBalancer Service or Ingress controller provisions an AWS load balancer that itself sits in a PUBLIC subnet, forwarding traffic inward to the private nodes — the cloud-level public/private split and the Kubernetes-level Service/Ingress routing are two layers of the same overall design, not competing or redundant concepts.