What Is a VPC?
A VPC (Virtual Private Cloud) is your isolated network on AWS. Every resource that needs a network interface (EC2 instances, RDS databases, Lambda functions (when VPC-attached), ECS tasks, ElastiCache clusters) lives in a VPC.
You control the IP address range, subnet layout, routing tables, internet access, and network access controls. It's the foundation of your AWS security posture.
Subnet Architecture
The standard production pattern uses three subnet tiers:
Public subnets
- Have a route to an Internet Gateway
- Resources get public IP addresses
- Use for: ALBs, NAT Gateways, bastion hosts (if you still use them)
Private subnets (with egress)
- No direct inbound from the internet
- Outbound internet access via NAT Gateway or VPC endpoints
- Use for: Application servers (ECS, Lambda), worker processes
Isolated subnets (no egress)
- No route to the internet at all
- Can only communicate with other resources in the VPC (or via VPC endpoints)
- Use for: Databases, caches, internal services that should never reach the internet
Multi-AZ layout
Deploy subnets across at least 2 Availability Zones for high availability:
VPC: 10.0.0.0/16
Public: 10.0.0.0/24 (AZ-a) | 10.0.1.0/24 (AZ-b) | 10.0.2.0/24 (AZ-c)
Private: 10.0.10.0/24 (AZ-a) | 10.0.11.0/24 (AZ-b) | 10.0.12.0/24 (AZ-c)
Isolated: 10.0.20.0/24 (AZ-a) | 10.0.21.0/24 (AZ-b) | 10.0.22.0/24 (AZ-c)
NAT Gateway vs VPC Endpoints
This is the most common cost and security decision in VPC design.
NAT Gateway
Allows private subnet resources to reach the internet (for calling external APIs, downloading packages, etc.):
- $0.045/hour (~$32/month per AZ) + $0.045/GB processed
- Required for any internet-bound traffic from private subnets
- Single point of egress. All traffic goes through it
VPC Endpoints (Gateway and Interface)
Connect directly to AWS services without going through the internet:
- Gateway endpoints (S3, DynamoDB): Free. No reason not to use them.
- Interface endpoints (most other services): $0.01/hour (~$7.20/month per AZ) + $0.01/GB processed
The cost trap
A NAT Gateway processing 1TB/month of traffic to S3 costs ~$45 in data processing fees alone. Plus the hourly charge. A free S3 gateway endpoint handles the same traffic at zero cost.
The security argument
VPC endpoints keep traffic on the AWS backbone. It never traverses the public internet. For regulated workloads (healthcare, finance), this matters for compliance.
Recommended approach: Use VPC endpoints for all AWS services your workloads access frequently (S3, DynamoDB, SQS, SNS, CloudWatch, ECR, etc.). Only deploy a NAT Gateway if you genuinely need to reach external (non-AWS) endpoints.
Security Groups vs NACLs
Security groups (stateful)
- Attached to individual resources (ENIs)
- Allow rules only (no explicit deny)
- Stateful: if you allow inbound, the response is automatically allowed outbound
- Can reference other security groups (e.g., "allow traffic from the ALB security group")
Network ACLs (stateless)
- Applied at the subnet level
- Allow and deny rules
- Stateless: you need explicit rules for both request and response
- Processed in rule-number order
In practice: Security groups do 95% of the work. NACLs are useful for subnet-level deny rules (block a known bad IP range) but most teams leave them at the default allow-all and rely on security groups.
Security group design
Reference security groups by ID rather than IP addresses:
ALB SG: Allow inbound 443 from 0.0.0.0/0
App SG: Allow inbound 8080 from ALB SG
Database SG: Allow inbound 5432 from App SG
Cache SG: Allow inbound 6379 from App SG
This way, when you add or remove app instances, the rules don't change. The security group membership handles it.
VPC Flow Logs
Capture metadata about network traffic for debugging and security monitoring:
<version> <account-id> <interface-id> <srcaddr> <dstaddr> <srcport> <dstport> <protocol> <packets> <bytes> <start> <end> <action> <log-status>
Send to CloudWatch Logs or S3. Use for:
- Debugging connectivity issues ("why can't my Lambda reach the database?")
- Security analysis (unexpected traffic patterns)
- Compliance auditing
Common Patterns
Hub-and-spoke with Transit Gateway
Central "hub" VPC (shared services, DNS, inspection) connected to "spoke" VPCs (workloads) via Transit Gateway. Scales to hundreds of VPCs.
VPC Peering
Direct connection between two VPCs. Simple, no additional hop. Good for 2-3 VPCs. Doesn't scale well because peering isn't transitive.
PrivateLink (VPC Endpoint Services)
Expose a service from one VPC to another without peering. The consumer creates an interface endpoint; traffic stays on AWS backbone. Good for SaaS products or shared microservices.
CDK Example
import { Vpc, SubnetType, SecurityGroup, Port, GatewayVpcEndpoint,
GatewayVpcEndpointAwsService, InterfaceVpcEndpoint,
InterfaceVpcEndpointAwsService } from 'aws-cdk-lib/aws-ec2';
const vpc = new Vpc(this, 'AppVpc', {
maxAzs: 3,
natGateways: 1, // or 0 if using only VPC endpoints
subnetConfiguration: [
{ name: 'public', subnetType: SubnetType.PUBLIC, cidrMask: 24 },
{ name: 'private', subnetType: SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24 },
{ name: 'isolated', subnetType: SubnetType.PRIVATE_ISOLATED, cidrMask: 24 },
],
});
// Free gateway endpoints
vpc.addGatewayEndpoint('S3Endpoint', {
service: GatewayVpcEndpointAwsService.S3,
});
vpc.addGatewayEndpoint('DynamoEndpoint', {
service: GatewayVpcEndpointAwsService.DYNAMODB,
});
// Interface endpoints for common services
vpc.addInterfaceEndpoint('SqsEndpoint', {
service: InterfaceVpcEndpointAwsService.SQS,
});
vpc.addInterfaceEndpoint('SecretsEndpoint', {
service: InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
});
Cost Considerations
| Component | Cost |
|---|---|
| VPC itself | Free |
| Subnets, route tables, security groups | Free |
| NAT Gateway | $0.045/hour + $0.045/GB |
| Interface VPC Endpoint | $0.01/hour/AZ + $0.01/GB |
| Gateway VPC Endpoint (S3, DynamoDB) | Free |
| VPC Peering | Free (data transfer charges apply) |
| Transit Gateway | $0.05/hour + $0.02/GB |
The biggest surprise for most teams: NAT Gateway costs. A multi-AZ deployment with moderate traffic can easily cost $200-500/month in NAT charges alone.
Further Reading
Related Blog Posts
Looking for hands-on help? View my AWS architecture services β