Amazon VPC Essentials

VPC essentials, network architecture, CLI commands, and best practices

@geomenaSun Jun 15 20251,017 views

Amazon VPC (Virtual Private Cloud) is your isolated private virtual network in AWS. It lets you define your own IP address space, create subnets, configure route tables, and control network traffic.

The problem it solves: Network isolation, layer segmentation (web/app/db), granular access control, and secure connectivity between cloud and on-premise resources.

When to use it: ALWAYS. Every EC2, RDS, Lambda (in VPC), etc. resource needs a VPC. The question isn't "should I use VPC?" but rather "how should I design my VPC?"

Key Concepts

ConceptDescription
CIDR Blocks (Classless Inter-Domain Routing)Notation that defines IP ranges. Format: 10.0.0.0/16
• The number after / indicates fixed bits (lower number = more IPs)
/16 = 65,536 IPs ∙ /20 = 4,096 IPs ∙ /24 = 256 IPs ∙ /32 = 1 IP
• AWS reserves 5 IPs per subnet (first 4 + last)
SubnetsNetwork segments within your VPC. They can be public or private.
Public: Has a route to the Internet Gateway + instances with public IPs
Private: No direct internet access (can use NAT Gateway for outbound)
Route TablesRouting tables that define where traffic goes.
Main Route Table: Default for all subnets without explicit association
Custom Route Table: Manually associated with specific subnets
• Common routes: 10.0.0.0/16 → local, 0.0.0.0/0 → igw-xxx
Internet Gateway (IGW)The gateway between your VPC and the internet. Enables bidirectional traffic.
• 1 IGW per VPC
• Requires attachment to the VPC
• Instances need public IPs to communicate
NAT GatewayAllows instances in private subnets to access the internet (outbound only).
• Location: Public subnet
• Requires an Elastic IP
• Unidirectional: private instances → internet (internet CANNOT initiate connections)
• Cost: ~0.045/hour+0.045/hour + 0.045/GB processed
Security GroupsStateful virtual firewalls at the instance level.
• Stateful: responses are automatically allowed
• Only ALLOW rules (no DENY)
• Can reference other Security Groups as source
• Default egress: allows all when you create the SG
Network ACLs (NACLs)Stateless firewalls at the subnet level (less common).
• Stateless: you must allow traffic in both directions
• Supports ALLOW and DENY rules
• Default: allows everything

Essential AWS CLI Commands

Creating Resources

# Create VPC
aws ec2 create-vpc \
  --cidr-block 10.0.0.0/16 \
  --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=my-vpc}]' \
  --region sa-east-1

# Create Subnet
aws ec2 create-subnet \
  --vpc-id vpc-xxxxx \
  --cidr-block 10.0.1.0/24 \
  --availability-zone sa-east-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-subnet}]'

# Create Internet Gateway
aws ec2 create-internet-gateway \
  --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=my-igw}]'

# Attach IGW to VPC
aws ec2 attach-internet-gateway \
  --internet-gateway-id igw-xxxxx \
  --vpc-id vpc-xxxxx

# Create NAT Gateway (requires Elastic IP first)
aws ec2 allocate-address --domain vpc  # Get AllocationId
aws ec2 create-nat-gateway \
  --subnet-id subnet-xxxxx \
  --allocation-id eipalloc-xxxxx

# Create Route Table
aws ec2 create-route-table \
  --vpc-id vpc-xxxxx \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=public-rt}]'

# Add route to Route Table
aws ec2 create-route \
  --route-table-id rtb-xxxxx \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id igw-xxxxx  # Or nat-gateway-id for NAT

# Associate Route Table with Subnet
aws ec2 associate-route-table \
  --route-table-id rtb-xxxxx \
  --subnet-id subnet-xxxxx

# Create Security Group
aws ec2 create-security-group \
  --group-name my-sg \
  --description "My security group" \
  --vpc-id vpc-xxxxx

# Add rule to Security Group
aws ec2 authorize-security-group-ingress \
  --group-id sg-xxxxx \
  --protocol tcp \
  --port 22 \
  --cidr 203.0.113.0/24  # Or --source-group sg-yyyyy

Querying Resources

# List VPCs
aws ec2 describe-vpcs

# List Subnets in a VPC
aws ec2 describe-subnets \
  --filters "Name=vpc-id,Values=vpc-xxxxx"

# View Route Tables
aws ec2 describe-route-tables \
  --filters "Name=vpc-id,Values=vpc-xxxxx"

# View Internet Gateways
aws ec2 describe-internet-gateways \
  --filters "Name=attachment.vpc-id,Values=vpc-xxxxx"

# View NAT Gateways
aws ec2 describe-nat-gateways \
  --filter "Name=vpc-id,Values=vpc-xxxxx"

# View Security Groups
aws ec2 describe-security-groups \
  --filters "Name=vpc-id,Values=vpc-xxxxx"

# View Security Group rules
aws ec2 describe-security-group-rules \
  --filters "Name=group-id,Values=sg-xxxxx"

Modifying Resources

# Modify subnet attribute (auto-assign public IP)
aws ec2 modify-subnet-attribute \
  --subnet-id subnet-xxxxx \
  --map-public-ip-on-launch

# Revoke Security Group rule
aws ec2 revoke-security-group-ingress \
  --group-id sg-xxxxx \
  --protocol tcp \
  --port 22 \
  --cidr 203.0.113.0/24

Deleting Resources

# Delete NAT Gateway
aws ec2 delete-nat-gateway --nat-gateway-id nat-xxxxx

# Release Elastic IP
aws ec2 release-address --allocation-id eipalloc-xxxxx

# Disassociate Route Table
aws ec2 disassociate-route-table --association-id rtbassoc-xxxxx

# Delete Route Table
aws ec2 delete-route-table --route-table-id rtb-xxxxx

# Detach Internet Gateway
aws ec2 detach-internet-gateway \
  --internet-gateway-id igw-xxxxx \
  --vpc-id vpc-xxxxx

# Delete Internet Gateway
aws ec2 delete-internet-gateway --internet-gateway-id igw-xxxxx

# Delete Subnet
aws ec2 delete-subnet --subnet-id subnet-xxxxx

# Delete Security Group
aws ec2 delete-security-group --group-id sg-xxxxx

# Delete VPC (must be empty)
aws ec2 delete-vpc --vpc-id vpc-xxxxx

Architecture and Flows

Typical Architecture Diagram

Traffic Flow Diagram

Decision: Public or Private Subnet?

Best Practices Checklist

Security

  • Use private subnets for resources that don't need public access (databases, app servers)
  • Apply least privilege principle in Security Groups (only necessary ports, specific source IPs)
  • Keep Main Route Table without an internet route (force explicit association for public subnets)
  • Enable VPC Flow Logs for traffic auditing
  • Don't use Default VPC in production (create custom VPCs with intentional design)
  • Use NACLs for defense in depth (though Security Groups are usually sufficient)

Cost Optimization

  • Delete unused NAT Gateways (0.045/hour=0.045/hour = 32/month per idle NAT)
  • Use NAT Gateway in high-availability subnet (avoid cross-AZ data transfer charges)
  • Release unassociated Elastic IPs ($0.005/hour per unused EIP)
  • Consider NAT Instance instead of NAT Gateway for dev/test environments (cheaper but requires management)
  • Monitor data transfer costs (NAT Gateway charges $0.045/GB processed)

Performance

  • Subnets in multiple Availability Zones for high availability
  • Appropriate subnet sizing (leave room for growth, consider AWS's 5 IP reservation)
  • Optimized Route Tables (avoid unnecessary routes that increase latency)
  • Use VPC Endpoints for S3/DynamoDB (avoid internet/NAT traffic, reduces latency and costs)

Reliability

  • Multi-AZ deployment (subnets in at least 2 AZs)
  • Redundant NAT Gateway (1 per AZ in production)
  • Route53 health checks for failover between AZs
  • Backup VPC configuration (use Infrastructure as Code - Terraform/CloudFormation)

Operational Excellence

  • Clear naming conventions (vpc-prod-web, subnet-public-1a, sg-bastion-prod)
  • Consistent tagging (Environment, Project, Owner, CostCenter)
  • Document network design (diagrams, CIDR allocations, route tables)
  • Use Infrastructure as Code (avoid manual configuration, facilitates DR)
  • VPC peering or Transit Gateway for inter-VPC communication (avoid public IPs)

Common Mistakes to Avoid

Main Route Table with internet route

Why it happens: When creating an IGW, you add a route to the Main RT thinking it only affects public subnets.

The problem: All subnets without an explicit route table (including private ones) become exposed to the internet.

How to avoid it:

  • Main RT with only local route
  • Create a custom RT with IGW route only for public subnets
  • Associate explicitly

Forgetting that NAT Gateway goes in a public subnet

Why it happens: Confusion about which subnet needs the NAT.

The problem: NAT Gateway in a private subnet can't reach the internet → private instances can't either.

How to avoid it:

  • NAT Gateway ALWAYS in a public subnet (the one with a route to IGW)
  • Private subnets have a 0.0.0.0/0 → nat-xxx route

Security Groups without egress rules

Why it happens: Manually adding an ingress rule can sometimes remove the default egress rule.

The problem: Instances can't respond to requests or make outbound connections.

How to avoid it:

  • Verify egress rules after modifying SG
  • Explicitly add: aws ec2 authorize-security-group-egress --protocol -1 --cidr 0.0.0.0/0

Overlapping CIDR blocks

Why it happens: Creating a VPC with the same CIDR as the Default VPC or on-premise network.

The problem: You can't create VPC Peering or VPN connections.

How to avoid it:

  • Use 10.x.x.x/16 for custom VPCs (avoid 172.31.x.x from Default VPC)
  • Document CIDR allocations across your organization
  • Plan before creating

Subnet too small

Why it happens: Using /28 (16 IPs) thinking it's enough for 10 instances.

The problem: AWS reserves 5 IPs → only 11 usable. No room to grow.

How to avoid it:

  • Use /24 (256 IPs) as the baseline for subnets
  • Remember: AWS reserves the first 4 IPs + the last one
  • Plan for 3-5x growth

Not releasing unassociated Elastic IPs

Why it happens: Deleting a NAT Gateway but forgetting about the EIP.

The problem: 0.005/hourcharge(0.005/hour charge (3.60/month) per idle EIP.

How to avoid it:

  • Check EIPs before deleting resources: aws ec2 describe-addresses
  • Release immediately after deleting NAT: aws ec2 release-address

Security Group referencing a changing public IP

Why it happens: Allowing SSH from a dynamic ISP IP, then the IP changes.

The problem: Loss of SSH access.

How to avoid it:

  • Use Elastic IP for critical access
  • Or use a Security Group as source (for bastion → private)
  • Or use AWS Systems Manager Session Manager (doesn't require SSH)

Cost Considerations

What generates costs in VPC

ResourceCostNotes
VPC, Subnets, Route Tables, IGWFREENo charge for these base resources
NAT Gateway$0.045/hour~$32/month per NAT Gateway
NAT Gateway - Data Processing$0.045/GBAll traffic that passes through NAT
Unassociated Elastic IP$0.005/hour~$3.60/month per idle EIP
Elastic IP associated with running instanceFREEOnly 1 free EIP per instance
VPC Peering - Data Transfer$0.01/GBBetween AZs in the same region
VPN Connection$0.05/hour~$36/month per VPN
Transit Gateway0.05/hour+0.05/hour + 0.02/GBAlternative to VPC Peering for many VPCs

How to optimize costs

  1. Delete NAT Gateways in dev/test during off-hours

    # Delete NAT Gateway at 8pm
    aws ec2 delete-nat-gateway --nat-gateway-id nat-xxxxx
    # Recreate at 8am (automation via Lambda + EventBridge)
  2. Use VPC Endpoints for S3/DynamoDB

    • Avoids traffic through NAT Gateway (saves $0.045/GB)
    • Reduces latency
    aws ec2 create-vpc-endpoint \
      --vpc-id vpc-xxxxx \
      --service-name com.amazonaws.us-east-1.s3 \
      --route-table-ids rtb-xxxxx
  3. NAT Instance instead of NAT Gateway (dev/test)

    • Use t3.nano (~3.80/month)insteadofNATGateway(3.80/month) instead of NAT Gateway (32/month)
    • Trade-off: requires management, less throughput
  4. Monitor data transfer with Cost Explorer

    • Filter by "Data Transfer" usage type
    • Identify top consumers
  5. Release unused resources

    # List unassociated EIPs (they cost money)
    aws ec2 describe-addresses \
      --query 'Addresses[?AssociationId==`null`]'

Free Tier Limits

  • VPC: Unlimited (no charge)
  • Subnets: Unlimited (no charge)
  • Route Tables: Unlimited (no charge)
  • Security Groups: 2,500 per region (no charge)
  • Internet Gateway: Unlimited (no charge)
  • NAT Gateway: NOT in free tier (charges from hour 1)
  • Elastic IPs: 5 per region (free if associated with running instances)

Integration with Other Services

AWS ServiceHow it integrates with VPCTypical use case
EC2Instances are launched in specific subnetsWeb/app servers in public/private subnets
RDSDB instances in subnet groups (multi-AZ)Database tier in private subnet, isolated from internet
LambdaCan run inside VPCAccess RDS or private resources from function
ELBLoad balancers in public or private subnetsALB in public subnet, distributes to EC2 in private
S3Via VPC Endpoint (without going to internet)Apps in private subnet access S3 without NAT Gateway
DynamoDBVia VPC EndpointReduce latency and data transfer costs
API GatewayPrivate API Gateway only accessible from VPCInternal APIs that shouldn't be exposed to internet
CloudFrontOrigin can be an ALB in VPCCDN that caches content from app in VPC
Direct ConnectConnects VPC with on-premise datacenterHybrid cloud + on-prem architectures
VPNVirtual Private Gateway in VPCSecure access from office to VPC resources
Route53Private Hosted Zones for internal DNSInternal resource DNS resolution (db.internal.com)
CloudWatchVPC Flow Logs for traffic monitoringDebugging SG rules, anomaly detection
IAMInstance Profiles for EC2 in VPCGrant permissions to instances without hardcoding keys

Additional Resources

Official Documentation

Relevant Whitepapers

Tools