AWS VPC
Amazon Virtual Private Cloud (VPC) is your own isolated virtual network inside AWS — the foundation every other AWS resource runs in. An EC2 instance, an RDS database, an EKS cluster: all live inside a VPC, and the VPC's subnets, routing, and firewall rules determine what can talk to what and what's reachable from the internet. Understanding VPC is understanding how AWS connectivity and network security actually work.
The core idea mirrors traditional data-center networking (cloud networking concepts apply directly): you carve an IP address range into subnets, decide which are public (internet-reachable) and which are private (internal-only), wire them together with route tables and gateways, and control traffic with security groups. Getting the public/private split right is the difference between a database safely hidden from the internet and one accidentally exposed to the world.
TL;DR
- A VPC is a private virtual network with an IP range (CIDR, e.g.
10.0.0.0/16) that your AWS resources launch into. - You divide it into subnets across availability zones; public subnets can reach the internet, private subnets cannot directly.
- Route tables decide where traffic goes; an internet gateway gives public subnets internet access; a NAT gateway lets private subnets make outbound connections without being reachable inbound.
- Security groups (stateful, instance-level firewalls) are your main traffic control; NACLs (stateless, subnet-level) are a coarser secondary layer.
- The standard secure pattern: public subnets for load balancers only; private subnets for app servers and databases.
- Everything is deny-by-default for inbound — you open exactly the ports you need, from exactly the sources you trust.
Core Concepts
Subnets and Availability Zones
A VPC spans a region; subnets are slices of its IP range, each pinned to one availability zone. For resilience you spread resources across multiple AZs (a subnet per AZ), so one AZ failure doesn't take you down. The critical property is public vs private:
- Public subnet — has a route to an internet gateway; resources here can have public IPs and be reached from the internet (or reach out to it).
- Private subnet — no direct internet route; resources are unreachable from the internet and reach out only via a NAT gateway.
Routing and Gateways
Route tables attached to subnets decide where traffic goes based on destination:
The NAT gateway is the key to the private-subnet pattern: your database can download patches and call AWS APIs (outbound) while remaining completely unreachable from the internet (no inbound).
Security Groups vs NACLs
Two firewall layers, often confused:
Security groups are your primary tool — stateful (allow a request in, its response goes out automatically), attached to instances, and the everyday way you say "the load balancer can reach the app on 443; the app can reach the DB on 5432." NACLs are a coarser subnet-wide backstop, useful for broad denies (block an IP range) but not your main control. A key pattern: security groups can reference other security groups as sources ("allow the app-sg to reach the db-sg"), which is cleaner than hardcoding IPs.
A Secure Baseline Architecture
The pattern that keeps databases off the internet:
The load balancer is the only internet-facing resource; everything else lives in private subnets, reachable only from the specific security groups that need it. This is defense in depth at the network layer — and the zero-trust instinct of "nothing is reachable unless explicitly permitted."
Common Mistakes
Databases in Public Subnets
The classic breach: an RDS instance or database in a public subnet with a security group open to 0.0.0.0/0, discoverable by internet scanners. Databases belong in private subnets with security groups allowing access only from your app's security group. If it's on the internet, it will be found.
Overly Open Security Groups
0.0.0.0/0 on port 22 (SSH), 3389 (RDP), or database ports exposes those services to the entire internet — constant brute-force target. Restrict sources to specific IPs, security groups, or use Systems Manager Session Manager for SSH-free access. Open only the ports you need, from the narrowest sources.
Confusing Security Groups and NACLs
Treating NACLs as the primary firewall (they're stateless, so you must remember return-traffic rules) or forgetting security groups are stateful. Use security groups as the main control; reserve NACLs for coarse subnet-level denies.
No NAT Gateway (or One Per AZ Missing)
Private instances that can't reach the internet fail to pull updates or call external APIs. You need a NAT gateway (in a public subnet) with private route tables pointing to it — ideally one per AZ for resilience (a single NAT gateway is an AZ-level single point of failure and a cross-AZ data-transfer cost).
Undersized or Overlapping CIDRs
Picking a small CIDR you outgrow, or a range that overlaps with other VPCs/on-prem networks you'll later need to peer with, causes painful re-architecture. Plan the IP space with room to grow and non-overlapping ranges. See cloud networking.
FAQ
What's the difference between a public and private subnet?
Only the routing: a public subnet has a route to an internet gateway (resources can be internet-reachable); a private subnet does not (resources are internet-isolated, reaching out only via a NAT gateway). There's no separate "public/private" setting — it's entirely determined by the route table. Put internet-facing things (load balancers) in public subnets and everything else (apps, databases) in private ones.
Security group or NACL — which do I use?
Security groups for almost everything — they're stateful (return traffic is automatic), instance-level, and can reference other security groups. Use NACLs only as a coarse subnet-wide backstop (e.g., blocking a malicious IP range across a whole subnet). Trying to manage fine-grained access with stateless NACLs is painful and error-prone.
How do private instances access the internet?
Through a NAT gateway in a public subnet, with the private subnet's route table sending internet-bound traffic to it. This allows outbound connections (pulling updates, calling APIs) while keeping the instances unreachable inbound from the internet. For AWS services specifically, VPC endpoints keep that traffic off the internet entirely.
Do I need to design a VPC from scratch?
Not always — AWS provides a default VPC, and tools (Terraform, CDK, the VPC wizard) generate the standard multi-AZ public/private layout. For production, use infrastructure as code to define a VPC with the public/private-subnet-per-AZ pattern rather than clicking it together. Understanding the pieces still matters for security and debugging.
How do I connect a VPC to other networks?
VPC Peering or Transit Gateway to connect VPCs to each other; VPN or Direct Connect to link to on-premises data centers; VPC Endpoints to reach AWS services privately. Transit Gateway is the scalable hub-and-spoke choice when connecting many VPCs and networks.
Related Topics
- AWS — The provider overview
- Cloud Networking — The concepts (subnets, routing, firewalls) generally
- AWS EC2 — Instances that launch into VPC subnets
- AWS RDS — Databases that belong in private subnets
- Load Balancing — The public-facing entry point
- Zero Trust — Network-layer "reachable only if permitted"
- Security Headers — Defense in depth above the network