On this page
VPC — Networking
Amazon Virtual Private Cloud (VPC) is your isolated network within AWS. Every EC2 instance, RDS database, and Lambda function in a VPC operates inside this software-defined network. Proper VPC design is the backbone of secure, scalable cloud architecture.
VPC Fundamentals
┌─────────────────────────────────────────────────────────┐
│ VPC (10.0.0.0/16) │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Public Subnet AZ-a │ │ Public Subnet AZ-b │ │
│ │ 10.0.1.0/24 │ │ 10.0.2.0/24 │ │
│ │ [ALB] [NAT GW] │ │ [ALB] [NAT GW] │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ Private Subnet AZ-a │ │ Private Subnet AZ-b │ │
│ │ 10.0.10.0/24 │ │ 10.0.11.0/24 │ │
│ │ [App EC2] [RDS] │ │ [App EC2] [RDS] │ │
│ └─────────────────────┘ └─────────────────────┘ │
│ Internet Gateway ← Public subnets only │
└─────────────────────────────────────────────────────────┘
| Component | Purpose |
|---|---|
| VPC | Isolated network with CIDR block (e.g., 10.0.0.0/16) |
| Subnet | Segment of VPC CIDR in one AZ |
| Internet Gateway (IGW) | Allows public subnet resources to reach internet |
| NAT Gateway | Outbound internet for private subnets |
| Route Table | Directs traffic (local, IGW, NAT, VPC peering) |
| Security Group | Stateful instance-level firewall |
| NACL | Stateless subnet-level firewall |
Create a VPC (CLI)
# Create VPC
aws ec2 create-vpc --cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=production-vpc}]'
# Create subnets in two AZs
aws ec2 create-subnet --vpc-id vpc-xxx --cidr-block 10.0.1.0/24 \
--availability-zone us-east-1a --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-1a}]'
aws ec2 create-subnet --vpc-id vpc-xxx --cidr-block 10.0.2.0/24 \
--availability-zone us-east-1b --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-1b}]'
aws ec2 create-subnet --vpc-id vpc-xxx --cidr-block 10.0.10.0/24 \
--availability-zone us-east-1a --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-1a}]'
aws ec2 create-subnet --vpc-id vpc-xxx --cidr-block 10.0.11.0/24 \
--availability-zone us-east-1b --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=private-1b}]'
# Internet Gateway
aws ec2 create-internet-gateway --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=prod-igw}]'
aws ec2 attach-internet-gateway --internet-gateway-id igw-xxx --vpc-id vpc-xxx
Route Tables
# Public route table: 0.0.0.0/0 → IGW
aws ec2 create-route-table --vpc-id vpc-xxx
aws ec2 create-route --route-table-id rtb-public --destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-xxx
aws ec2 associate-route-table --route-table-id rtb-public --subnet-id subnet-public-1a
# NAT Gateway in public subnet (requires Elastic IP)
aws ec2 allocate-address --domain vpc
aws ec2 create-nat-gateway --subnet-id subnet-public-1a --allocation-id eipalloc-xxx
# Private route table: 0.0.0.0/0 → NAT GW
aws ec2 create-route --route-table-id rtb-private --destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id nat-xxx
Cost note: NAT Gateway charges ~$0.045/hour + data processing. For dev, consider NAT Instance or VPC endpoints to reduce costs.
Security Groups vs NACLs
| Feature | Security Group | NACL |
|---|---|---|
| Level | Instance/ENI | Subnet |
| State | Stateful (return traffic auto-allowed) | Stateless (must allow both directions) |
| Rules | Allow only | Allow and Deny |
| Default | Deny all inbound | Allow all |
| Use case | Primary access control | Additional subnet-level deny rules |
# App server SG — allow HTTP from ALB SG only
aws ec2 authorize-security-group-ingress \
--group-id sg-app \
--protocol tcp --port 8080 \
--source-group sg-alb
# Database SG — allow PostgreSQL from app SG only
aws ec2 authorize-security-group-ingress \
--group-id sg-database \
--protocol tcp --port 5432 \
--source-group sg-app
VPC Endpoints (Private AWS Access)
Avoid NAT Gateway costs for AWS service traffic:
# Gateway endpoint for S3 (free)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-xxx \
--service-name com.amazonaws.us-east-1.s3 \
--route-table-ids rtb-private
# Interface endpoint for Secrets Manager (~$0.01/hour per AZ)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-xxx \
--service-name com.amazonaws.us-east-1.secretsmanager \
--vpc-endpoint-type Interface \
--subnet-ids subnet-private-1a subnet-private-1b \
--security-group-ids sg-endpoints
VPC Peering and Transit Gateway
| Option | Use Case | Limitation |
|---|---|---|
| VPC Peering | Two VPCs, full mesh small | No transitive routing |
| Transit Gateway | Hub-and-spoke, many VPCs | Additional cost |
| VPN / Direct Connect | Hybrid cloud (on-premises) | Setup complexity |
# VPC Peering
aws ec2 create-vpc-peering-connection \
--vpc-id vpc-prod --peer-vpc-id vpc-shared-services
aws ec2 accept-vpc-peering-connection --vpc-peering-connection-id pcx-xxx
# Add route in both VPCs
aws ec2 create-route --route-table-id rtb-prod \
--destination-cidr-block 10.1.0.0/16 --vpc-peering-connection-id pcx-xxx
CIDR Planning Best Practices
| VPC Size | CIDR | Usable IPs | Recommendation |
|---|---|---|---|
| Small | /24 | 256 | Dev/test only |
| Medium | /20 | 4,096 | Single app production |
| Large | /16 | 65,536 | Multi-app, room to grow |
| Enterprise | /12 | 1M+ | Multi-region, many services |
Reserve space for:
- Public subnets: /24 per AZ
- Private app subnets: /24 per AZ
- Private DB subnets: /24 per AZ
- Future expansion and peering (non-overlapping CIDRs)
Real-World Scenario: Production VPC
| Subnet | CIDR | AZ | Resources |
|---|---|---|---|
| public-web-1a | 10.0.1.0/24 | us-east-1a | ALB, NAT GW |
| public-web-1b | 10.0.2.0/24 | us-east-1b | ALB, NAT GW |
| private-app-1a | 10.0.10.0/24 | us-east-1a | EC2 ASG, ECS |
| private-app-1b | 10.0.11.0/24 | us-east-1b | EC2 ASG, ECS |
| private-db-1a | 10.0.20.0/24 | us-east-1a | RDS primary |
| private-db-1b | 10.0.21.0/24 | us-east-1b | RDS standby |
Common Mistakes
- Single AZ deployment — always span at least two AZs for production
- Overlapping CIDRs — plan before peering or connecting to on-premises
- One NAT Gateway — single point of failure; use one per AZ in production
- Public RDS/database subnets — databases belong in private subnets
- Default VPC for production — create purpose-built VPCs with proper segmentation
- Ignoring VPC Flow Logs — enable for security monitoring and troubleshooting
Troubleshooting
| Issue | Check | Fix |
|---|---|---|
| Instance can’t reach internet | Route table, IGW, public IP | Verify 0.0.0.0/0 → IGW on public subnet |
| Private instance no outbound | NAT GW route, NAT in public subnet | Add 0.0.0.0/0 → NAT in private route table |
| Can’t connect to RDS | SG, subnet group, NACL | SG must allow source on DB port |
| VPC peering no traffic | Routes in both VPCs, SG | Add peering routes; check SG allows peer CIDR |
| High NAT costs | All AWS API via NAT | Add VPC endpoints for S3, DynamoDB, etc. |
Enable VPC Flow Logs
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-xxx \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name /aws/vpc/flowlogs/production
Best Practices Summary
- Use private subnets for application and database tiers
- Deploy NAT Gateway per AZ for production HA
- Implement VPC endpoints to reduce NAT costs and improve security
- Apply least-privilege security groups — reference SG IDs, not CIDRs where possible
- Enable VPC Flow Logs and send to CloudWatch or S3
- Plan non-overlapping CIDR blocks before multi-VPC architecture
- Use AWS Network Firewall or WAF for advanced threat protection
Next: Lambda — Serverless.