AWS VPC Public and Private Subnet Design and Configuration Guide

🌏 閱讀中文版本


Why Do We Need Public and Private Subnets?

Core Design Philosophy of AWS VPC Network Architecture

In Amazon Web Services (AWS) Virtual Private Cloud (VPC) architecture, the distinction between Public and Private Subnets isn’t arbitrary—it’s grounded in the security best practices of “principle of least privilege” and “defense in depth.” This network layering design effectively reduces attack surfaces, protects critical resources, while maintaining necessary external connectivity.

1. Layered Security Protection

Public Subnet Exposure Risks:

  • Directly exposed to the internet, facing higher attack risks (DDoS, scanning, brute force)
  • Requires robust security measures (Security Groups, NACLs, WAF)
  • Suitable for components that “must be internet-facing” (Load Balancers, NAT Gateways, Bastion Hosts)

Private Subnet Isolation Protection:

  • Completely isolated from direct internet access, significantly reducing attack surface
  • Accessible only through specific channels (Bastion Host, VPN, AWS Systems Manager)
  • Suitable for “should not be public” sensitive resources (Databases, Application Servers, Internal Services)

Real-world Case: In 2023, a company suffered a data breach because they placed their RDS database in a Public Subnet with 0.0.0.0/0 access. Using Private Subnet + strict Security Groups would have completely prevented this incident.

2. Cost Optimization and Performance Enhancement

  • Centralized NAT Gateway management: All instances in Private Subnets share NAT Gateway for outbound traffic, saving Elastic IP costs
  • VPC Endpoint saves data transfer fees: Private Subnets can access AWS services (S3, DynamoDB) via VPC Endpoints without internet traffic, eliminating data transfer costs
  • Reduced bandwidth costs: Internal service communication stays within VPC, avoiding expensive internet traffic fees

3. Compliance Requirements

Many compliance standards (GDPR, HIPAA, PCI-DSS) require:

  • Sensitive data must be stored in isolated environments
  • Databases must not be directly exposed to the internet
  • Clear network access controls and audit trails are mandatory

Private Subnet design naturally meets these requirements, greatly simplifying compliance verification.


Public Subnet Explained

Definition and Characteristics

A Public Subnet is one where EC2 instances can directly communicate bidirectionally with the internet. Key characteristics:

  • ✅ Route table contains a route to Internet Gateway (IGW) (0.0.0.0/0 → IGW)
  • ✅ Instances have Public IP or Elastic IP (EIP)
  • ✅ Security Group allows inbound traffic from the internet (specific ports)
  • ✅ Network ACL allows inbound/outbound traffic

Complete Configuration Steps

1. Create VPC and Internet Gateway

# Create VPC
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=MyVPC}]'

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

# Attach IGW to VPC
aws ec2 attach-internet-gateway --vpc-id vpc-xxxxxxxx --internet-gateway-id igw-yyyyyyyy

2. Create Public Subnet

# Create Public Subnet (using availability zone us-east-1a)
aws ec2 create-subnet \
  --vpc-id vpc-xxxxxxxx \
  --cidr-block 10.0.1.0/24 \
  --availability-zone us-east-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=PublicSubnet-1A}]'

# Enable auto-assign public IP
aws ec2 modify-subnet-attribute \
  --subnet-id subnet-xxxxxxxx \
  --map-public-ip-on-launch

3. Configure Route Table

# Create route table
aws ec2 create-route-table \
  --vpc-id vpc-xxxxxxxx \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=PublicRouteTable}]'

# Add route to IGW
aws ec2 create-route \
  --route-table-id rtb-xxxxxxxx \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id igw-yyyyyyyy

# Associate route table with Public Subnet
aws ec2 associate-route-table \
  --route-table-id rtb-xxxxxxxx \
  --subnet-id subnet-xxxxxxxx

4. Configure Security Group

# Create Security Group (allow HTTP/HTTPS inbound)
aws ec2 create-security-group \
  --group-name WebServerSG \
  --description "Allow HTTP/HTTPS from internet" \
  --vpc-id vpc-xxxxxxxx

# Allow HTTP inbound
aws ec2 authorize-security-group-ingress \
  --group-id sg-xxxxxxxx \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0

# Allow HTTPS inbound
aws ec2 authorize-security-group-ingress \
  --group-id sg-xxxxxxxx \
  --protocol tcp \
  --port 443 \
  --cidr 0.0.0.0/0

# Allow SSH (recommend restricting source IP)
aws ec2 authorize-security-group-ingress \
  --group-id sg-xxxxxxxx \
  --protocol tcp \
  --port 22 \
  --cidr YOUR_IP/32

Use Cases

  1. Web Servers: Apache, Nginx, IIS that must receive user requests directly
  2. Application Load Balancer (ALB): Distribute traffic to backend application servers
  3. NAT Gateway: Provide outbound internet access for Private Subnets
  4. Bastion Host (Jump Server): Secure gateway to access Private Subnet resources
  5. VPN Gateway: Connection endpoint between on-premises network and AWS VPC

Private Subnet Explained

Definition and Characteristics

A Private Subnet is one where EC2 instances cannot directly communicate bidirectionally with the internet. Key characteristics:

  • ✅ Route table “does not contain” route to Internet Gateway
  • ✅ Instances “not assigned” Public IP (Private IP only)
  • ✅ Outbound internet access requires NAT Gateway or NAT Instance
  • ✅ Inbound access only through VPC internal or VPN/Direct Connect

Complete Configuration Steps

1. Create Private Subnet

# Create Private Subnet (using availability zone us-east-1a)
aws ec2 create-subnet \
  --vpc-id vpc-xxxxxxxx \
  --cidr-block 10.0.10.0/24 \
  --availability-zone us-east-1a \
  --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=PrivateSubnet-1A}]'

# Verify auto-assign public IP is disabled (default is off)
aws ec2 describe-subnets --subnet-ids subnet-xxxxxxxx --query 'Subnets[0].MapPublicIpOnLaunch'

2. Create NAT Gateway (enable Private Subnet outbound internet access)

# Allocate Elastic IP first
aws ec2 allocate-address --domain vpc

# Create NAT Gateway (must be in Public Subnet)
aws ec2 create-nat-gateway \
  --subnet-id subnet-PUBLIC-SUBNET-ID \
  --allocation-id eipalloc-xxxxxxxx \
  --tag-specifications 'ResourceType=nat-gateway,Tags=[{Key=Name,Value=MyNATGateway}]'

3. Configure Private Subnet Route Table

# Create route table
aws ec2 create-route-table \
  --vpc-id vpc-xxxxxxxx \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=PrivateRouteTable}]'

# Add route to NAT Gateway
aws ec2 create-route \
  --route-table-id rtb-xxxxxxxx \
  --destination-cidr-block 0.0.0.0/0 \
  --nat-gateway-id nat-xxxxxxxx

# Associate route table with Private Subnet
aws ec2 associate-route-table \
  --route-table-id rtb-xxxxxxxx \
  --subnet-id subnet-xxxxxxxx

4. Configure Security Group (allow VPC internal traffic only)

# Create Security Group (allow traffic from VPC only)
aws ec2 create-security-group \
  --group-name PrivateAppSG \
  --description "Allow traffic only from VPC" \
  --vpc-id vpc-xxxxxxxx

# Allow HTTP traffic from Public Subnet (e.g., from ALB)
aws ec2 authorize-security-group-ingress \
  --group-id sg-xxxxxxxx \
  --protocol tcp \
  --port 8080 \
  --source-group sg-PUBLIC-SG-ID

# Allow SSH from Bastion Host
aws ec2 authorize-security-group-ingress \
  --group-id sg-xxxxxxxx \
  --protocol tcp \
  --port 22 \
  --source-group sg-BASTION-SG-ID

Use Cases

  1. Database Servers: RDS, Aurora, self-hosted MySQL/PostgreSQL/MongoDB
  2. Application Servers: Backend APIs, microservices, business logic processing
  3. Cache Layer: ElastiCache (Redis/Memcached)
  4. Message Queues: Amazon MQ, RabbitMQ, Kafka
  5. Batch Processing Servers: Data analytics, ETL jobs, scheduled tasks

Public vs Private Subnet Comparison

Feature Public Subnet Private Subnet
Internet Gateway ✅ Route table points to IGW ❌ Route table doesn’t point to IGW
Public IP ✅ Auto-assigned or manually bind EIP ❌ Private IP only
Inbound internet access ✅ Allowed (requires Security Group opening) ❌ Blocked
Outbound internet access ✅ Direct via IGW ✅ Via NAT Gateway/Instance
Security risk ⚠️ High (directly exposed) ✅ Low (isolated protection)
Suitable resources ALB, Web Server, Bastion RDS, App Server, Cache
NAT Gateway cost Not required Required ($0.045/hour + data fees)
VPC Endpoint Optional Highly recommended (save data fees)

Frequently Asked Questions (FAQ)

Q1: How do EC2 instances in Private Subnets update software packages?

Answer: Via NAT Gateway or VPC Endpoints

Solution 1: Use NAT Gateway (for general internet access)

# Run on EC2 in Private Subnet
sudo yum update -y  # Amazon Linux
sudo apt update && sudo apt upgrade -y  # Ubuntu

# Traffic path: EC2 → NAT Gateway → Internet Gateway → Internet

Solution 2: Use VPC Endpoints (for AWS services, no data transfer fees)

# Create S3 VPC Endpoint (Gateway Endpoint, free)
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxxxxxxx \
  --service-name com.amazonaws.us-east-1.s3 \
  --route-table-ids rtb-PRIVATE-ROUTE-TABLE

# Create Systems Manager VPC Endpoint (Interface Endpoint, $0.01/hour)
aws ec2 create-vpc-endpoint \
  --vpc-id vpc-xxxxxxxx \
  --vpc-endpoint-type Interface \
  --service-name com.amazonaws.us-east-1.ssm \
  --subnet-ids subnet-xxxxxxxx \
  --security-group-ids sg-xxxxxxxx

Cost comparison:

  • NAT Gateway: $0.045/hour + $0.045/GB data ≈ $33/month + data fees
  • VPC Endpoint (S3): Free
  • VPC Endpoint (Interface): $0.01/hour ≈ $7.2/month

Q2: How to connect to databases in Private Subnets from local machine?

Answer: Three common solutions

Solution 1: Via Bastion Host (Jump Server)

# SSH tunnel connection
ssh -i mykey.pem -L 3306:rds-endpoint.amazonaws.com:3306 ec2-user@BASTION-PUBLIC-IP

# Connect database locally
mysql -h 127.0.0.1 -P 3306 -u admin -p

Solution 2: Use AWS Systems Manager Session Manager (no Bastion Host needed)

# Install Session Manager Plugin
curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/mac/sessionmanager-bundle.zip" -o "sessionmanager-bundle.zip"
unzip sessionmanager-bundle.zip
sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin

# Create Port Forwarding via SSM
aws ssm start-session \
  --target i-PRIVATE-INSTANCE-ID \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters '{"host":["rds-endpoint.amazonaws.com"],"portNumber":["3306"], "localPortNumber":["3306"]}'

# Connect database locally
mysql -h 127.0.0.1 -P 3306 -u admin -p

Solution 3: Use VPN or Direct Connect (enterprise solution)

  • Create AWS Client VPN Endpoint
  • Connect to VPC via VPN locally
  • Directly access Private Subnet resources

Q3: Will NAT Gateway failure affect services in Private Subnets?

Answer: Depends on service type

Unaffected scenarios:

  • ✅ VPC internal communication (e.g., ALB → App Server → RDS)
  • ✅ AWS services via VPC Endpoint (S3, DynamoDB)
  • ✅ Long-running established connections (if no new outbound needed)

Affected scenarios:

  • ❌ External API access required (e.g., third-party payment, SMS services)
  • ❌ Downloading internet resources (apt/yum update, Docker pull)
  • ❌ Sending external notifications (Email, Webhooks)

High availability solution:

# Create independent NAT Gateway in each AZ
# AZ 1a
aws ec2 create-nat-gateway --subnet-id subnet-public-1a --allocation-id eipalloc-1a

# AZ 1b
aws ec2 create-nat-gateway --subnet-id subnet-public-1b --allocation-id eipalloc-1b

# Each Private Subnet route table points to NAT Gateway in same AZ
# PrivateSubnet-1A → NAT-1A
# PrivateSubnet-1B → NAT-1B

Cost impact: Each NAT Gateway $33/month, dual-AZ deployment = $66/month + data fees

Q4: Are resources in Public Subnets necessarily insecure?

Answer: Not necessarily, proper configuration is key

Public Subnet security configuration best practices:

  1. Minimize open ports
    # ✅ Open necessary ports only
    Security Group: Allow 80, 443 (HTTP/HTTPS)
    Deny: 22 (SSH), 3389 (RDP), 3306 (MySQL)
    
    # ❌ Dangerous configuration
    0.0.0.0/0:0-65535 ALLOW
    
  2. Use ALB/NLB as frontend
    • Place ALB/NLB in Public Subnet
    • Place actual application servers in Private Subnet
    • ALB provides WAF, SSL Termination, DDoS protection
  3. Enable VPC Flow Logs
    # Create Flow Logs (monitor suspicious traffic)
    aws ec2 create-flow-logs \
      --resource-type VPC \
      --resource-ids vpc-xxxxxxxx \
      --traffic-type ALL \
      --log-destination-type cloud-watch-logs \
      --log-group-name /aws/vpc/flowlogs
    
  4. Regular security scanning
    • Use AWS Inspector to scan vulnerabilities
    • Use GuardDuty to detect anomalies
    • Use Security Hub for centralized security management

Q5: Can RDS be placed in Public Subnets?

Answer: Technically possible, but strongly discouraged

Why not recommended:

  • Violates least privilege principle: Databases don’t need direct internet access
  • Increases attack surface: Exposed to internet scanning and attacks
  • Compliance issues: Violates PCI-DSS, HIPAA standards
  • Accidental risks: Misconfigured Security Groups may lead to data leaks

Correct architecture:

Internet
   ↓
Internet Gateway
   ↓
ALB (Public Subnet)
   ↓
App Server (Private Subnet)
   ↓
RDS (Private Subnet - using DB Subnet Group)

Exceptions:

  • Dev/test environments requiring external tool direct access (not recommended, use Bastion or SSM)
  • Temporary POC validation (must migrate to Private Subnet after testing)

Q6: How to choose between NAT Gateway and NAT Instance?

Answer: NAT Gateway recommended in most cases

Feature NAT Gateway (Recommended) NAT Instance
Availability ✅ AWS-managed, highly available ❌ Single point of failure, manual HA setup
Performance ✅ Up to 100 Gbps ⚠️ Limited by instance type
Maintenance cost ✅ Zero maintenance ❌ Self-managed, patching required
Cost ⚠️ $0.045/hour + data fees ✅ EC2 cost only
Use case Production environments Dev/test, cost-sensitive

NAT Instance setup example (reference only):

# Launch NAT Instance (using AWS official NAT AMI)
aws ec2 run-instances \
  --image-id ami-xxxxxxxxxxxx \
  --instance-type t3.micro \
  --subnet-id subnet-PUBLIC-SUBNET \
  --security-group-ids sg-xxxxxxxx

# Disable source/destination check (required)
aws ec2 modify-instance-attribute \
  --instance-id i-xxxxxxxx \
  --no-source-dest-check

# Modify Private Subnet route table to point to NAT Instance
aws ec2 create-route \
  --route-table-id rtb-xxxxxxxx \
  --destination-cidr-block 0.0.0.0/0 \
  --instance-id i-NAT-INSTANCE-ID

Q7: How to configure Subnets in VPC Peering or Transit Gateway environments?

Answer: Adjust route tables to support cross-VPC communication

Scenario: VPC-A and VPC-B connected via VPC Peering

# VPC-A (10.0.0.0/16) ←→ VPC-B (10.1.0.0/16)

# Add route to VPC-B in VPC-A's Private Subnet route table
aws ec2 create-route \
  --route-table-id rtb-VPC-A-PRIVATE \
  --destination-cidr-block 10.1.0.0/16 \
  --vpc-peering-connection-id pcx-xxxxxxxx

# Add route to VPC-A in VPC-B's Private Subnet route table
aws ec2 create-route \
  --route-table-id rtb-VPC-B-PRIVATE \
  --destination-cidr-block 10.0.0.0/16 \
  --vpc-peering-connection-id pcx-xxxxxxxx

Transit Gateway architecture (recommended for multi-VPC environments):

VPC-A (10.0.0.0/16)  ─┐
VPC-B (10.1.0.0/16)  ─┼─→ Transit Gateway ←─ On-Premises (192.168.0.0/16)
VPC-C (10.2.0.0/16)  ─┘
# Add routes to TGW in each VPC's route tables
aws ec2 create-route \
  --route-table-id rtb-VPC-A-PRIVATE \
  --destination-cidr-block 0.0.0.0/0 \
  --transit-gateway-id tgw-xxxxxxxx

Best Practices

Architecture Design

  1. Three-tier architecture standard configuration
    Public Subnet:  ALB, NAT Gateway, Bastion Host
    Private Subnet: Application Servers, Lambda (in VPC)
    Data Subnet:    RDS, ElastiCache, Redshift (stricter Security Groups)
    
  2. Multi-AZ deployment (high availability)
    • Configure Public and Private Subnets in each AZ
    • Configure independent NAT Gateway in each AZ
    • Use RDS Multi-AZ deployment
  3. CIDR planning principles
    VPC:            10.0.0.0/16  (65536 IPs)
    Public-1A:      10.0.1.0/24  (256 IPs)
    Public-1B:      10.0.2.0/24
    Private-1A:     10.0.10.0/24
    Private-1B:     10.0.11.0/24
    Data-1A:        10.0.20.0/24
    Data-1B:        10.0.21.0/24
    Reserved:       10.0.100.0/22 (future expansion)
    

Security

  1. Security Group minimization principle
    • Open necessary ports only
    • Use Security Group IDs instead of CIDR (e.g., allow traffic from sg-alb)
    • Prohibit 0.0.0.0/0 access to sensitive ports
  2. Network ACL as second line of defense
    # Block known malicious IP ranges
    aws ec2 create-network-acl-entry \
      --network-acl-id acl-xxxxxxxx \
      --rule-number 10 \
      --protocol -1 \
      --rule-action deny \
      --cidr-block 123.45.67.0/24
    
  3. Enable VPC Flow Logs and GuardDuty

Cost Optimization

  1. Use VPC Endpoints instead of NAT Gateway (for AWS services)
    • S3, DynamoDB use Gateway Endpoint (free)
    • Other services use Interface Endpoint ($0.01/hour, but saves data fees)
  2. Evaluate NAT Gateway necessity
    • If only AWS services access needed → Use VPC Endpoint
    • If external API access needed → Keep NAT Gateway
  3. Use Reserved Capacity (long-term use)

Operations Management

  1. Use AWS Systems Manager instead of Bastion Host
    • No jump server maintenance needed
    • Complete audit logs
    • Supports Session Manager, Port Forwarding
  2. Automated backup and disaster recovery
    • Manage infrastructure with Terraform/CloudFormation
    • Regularly test disaster recovery procedures
  3. Monitoring and alerts
    • Monitor NAT Gateway traffic and error rates
    • Monitor VPC Flow Logs for abnormal traffic
    • Set CloudWatch Alarm notifications

Real-world Case: Complete Three-tier Architecture Deployment

Architecture Diagram

                        Internet
                            ↓
                    Internet Gateway
                            ↓
        ┌───────────────────┴───────────────────┐
        │           Public Subnet                │
        │  - ALB (443, 80)                       │
        │  - NAT Gateway (AZ-1A, AZ-1B)          │
        │  - Bastion Host (SSH corporate IP only)│
        └───────────────────┬───────────────────┘
                            ↓
        ┌───────────────────┴───────────────────┐
        │          Private Subnet                │
        │  - App Servers (Auto Scaling)          │
        │  - Lambda Functions (in VPC)           │
        │  - ElastiCache Redis                   │
        └───────────────────┬───────────────────┘
                            ↓
        ┌───────────────────┴───────────────────┐
        │           Data Subnet                  │
        │  - RDS Aurora (Multi-AZ)               │
        │  - Redshift Cluster                    │
        └───────────────────────────────────────┘

Complete Deployment Script (using AWS CLI)

#!/bin/bash
# Three-tier architecture deployment script

# Variable configuration
VPC_CIDR="10.0.0.0/16"
PUBLIC_SUBNET_1A="10.0.1.0/24"
PUBLIC_SUBNET_1B="10.0.2.0/24"
PRIVATE_SUBNET_1A="10.0.10.0/24"
PRIVATE_SUBNET_1B="10.0.11.0/24"
DATA_SUBNET_1A="10.0.20.0/24"
DATA_SUBNET_1B="10.0.21.0/24"

# 1. Create VPC
VPC_ID=$(aws ec2 create-vpc --cidr-block $VPC_CIDR --query 'Vpc.VpcId' --output text)
aws ec2 create-tags --resources $VPC_ID --tags Key=Name,Value=Production-VPC

# 2. Create Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway --query 'InternetGateway.InternetGatewayId' --output text)
aws ec2 attach-internet-gateway --vpc-id $VPC_ID --internet-gateway-id $IGW_ID

# 3. Create Public Subnets
PUBLIC_SUBNET_1A_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block $PUBLIC_SUBNET_1A --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text)
PUBLIC_SUBNET_1B_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block $PUBLIC_SUBNET_1B --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text)

# 4. Create Private Subnets
PRIVATE_SUBNET_1A_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block $PRIVATE_SUBNET_1A --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text)
PRIVATE_SUBNET_1B_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block $PRIVATE_SUBNET_1B --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text)

# 5. Create Data Subnets
DATA_SUBNET_1A_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block $DATA_SUBNET_1A --availability-zone us-east-1a --query 'Subnet.SubnetId' --output text)
DATA_SUBNET_1B_ID=$(aws ec2 create-subnet --vpc-id $VPC_ID --cidr-block $DATA_SUBNET_1B --availability-zone us-east-1b --query 'Subnet.SubnetId' --output text)

# 6. Create NAT Gateways (one per AZ)
EIP_1A=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
EIP_1B=$(aws ec2 allocate-address --domain vpc --query 'AllocationId' --output text)
NAT_1A=$(aws ec2 create-nat-gateway --subnet-id $PUBLIC_SUBNET_1A_ID --allocation-id $EIP_1A --query 'NatGateway.NatGatewayId' --output text)
NAT_1B=$(aws ec2 create-nat-gateway --subnet-id $PUBLIC_SUBNET_1B_ID --allocation-id $EIP_1B --query 'NatGateway.NatGatewayId' --output text)

# Wait for NAT Gateway creation
aws ec2 wait nat-gateway-available --nat-gateway-ids $NAT_1A $NAT_1B

# 7. Configure route tables
# Public Route Table
PUBLIC_RT=$(aws ec2 create-route-table --vpc-id $VPC_ID --query 'RouteTable.RouteTableId' --output text)
aws ec2 create-route --route-table-id $PUBLIC_RT --destination-cidr-block 0.0.0.0/0 --gateway-id $IGW_ID
aws ec2 associate-route-table --route-table-id $PUBLIC_RT --subnet-id $PUBLIC_SUBNET_1A_ID
aws ec2 associate-route-table --route-table-id $PUBLIC_RT --subnet-id $PUBLIC_SUBNET_1B_ID

# Private Route Tables (independent per AZ)
PRIVATE_RT_1A=$(aws ec2 create-route-table --vpc-id $VPC_ID --query 'RouteTable.RouteTableId' --output text)
aws ec2 create-route --route-table-id $PRIVATE_RT_1A --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $NAT_1A
aws ec2 associate-route-table --route-table-id $PRIVATE_RT_1A --subnet-id $PRIVATE_SUBNET_1A_ID

PRIVATE_RT_1B=$(aws ec2 create-route-table --vpc-id $VPC_ID --query 'RouteTable.RouteTableId' --output text)
aws ec2 create-route --route-table-id $PRIVATE_RT_1B --destination-cidr-block 0.0.0.0/0 --nat-gateway-id $NAT_1B
aws ec2 associate-route-table --route-table-id $PRIVATE_RT_1B --subnet-id $PRIVATE_SUBNET_1B_ID

# Data Route Table (no internet access)
DATA_RT=$(aws ec2 create-route-table --vpc-id $VPC_ID --query 'RouteTable.RouteTableId' --output text)
aws ec2 associate-route-table --route-table-id $DATA_RT --subnet-id $DATA_SUBNET_1A_ID
aws ec2 associate-route-table --route-table-id $DATA_RT --subnet-id $DATA_SUBNET_1B_ID

echo "VPC deployment complete!"
echo "VPC ID: $VPC_ID"
echo "Public Subnets: $PUBLIC_SUBNET_1A_ID, $PUBLIC_SUBNET_1B_ID"
echo "Private Subnets: $PRIVATE_SUBNET_1A_ID, $PRIVATE_SUBNET_1B_ID"
echo "Data Subnets: $DATA_SUBNET_1A_ID, $DATA_SUBNET_1B_ID"

Conclusion

AWS VPC’s Public and Private Subnet design is the cornerstone of cloud architecture security and reliability. Correctly understanding and configuring these two subnet types effectively:

  • 🔒 Enhances security: Reduces attack surface through network isolation, protects sensitive resources
  • 💰 Optimizes costs: Uses VPC Endpoints to reduce data transfer fees, properly configures NAT Gateways
  • Ensures performance: Proper network architecture avoids unnecessary hops and latency
  • Meets compliance: Satisfies GDPR, HIPAA, PCI-DSS regulatory requirements

Key takeaways:

  1. ✅ Public Subnets only for “must be internet-facing” resources (ALB, NAT Gateway, Bastion)
  2. ✅ Private Subnets for “should not be public” sensitive resources (RDS, App Server, Cache)
  3. ✅ Use NAT Gateway instead of NAT Instance (unless extremely cost-sensitive)
  4. ✅ Multi-AZ deployment for high availability (independent NAT Gateway per AZ)
  5. ✅ Use VPC Endpoints to reduce costs (S3, DynamoDB free, other services save data fees)
  6. ✅ Never place RDS in Public Subnets
  7. ✅ Use Security Group IDs instead of CIDR for rules (easier management)
  8. ✅ Enable VPC Flow Logs and GuardDuty to monitor security threats

Through this article’s detailed explanations, real-world cases, and FAQ, you should be able to design and deploy enterprise-grade AWS VPC network architecture. Remember: Security design starts at the network layer, and proper Public and Private Subnet configuration is the first line of defense in cloud security.

Related Articles

Leave a Comment