What Is Amazon VPC?
Amazon Virtual Private Cloud (Amazon VPC) is an AWS networking service that lets you create a logically isolated virtual network for cloud resources. You choose IP address ranges, define subnets, configure routing, and control which systems can communicate. Services such as Amazon EC2, Amazon RDS, ECS, and EKS can use your VPC to connect applications, databases, and infrastructure components through private networking.
VPC is foundational to AWS system architecture, but creating a VPC does not automatically create a secure or highly available application. Engineers must understand how CIDR blocks, subnets, internet gateways, NAT gateways, route tables, security groups, and VPC endpoints interact. A routing mistake can prevent an application from reaching its database, expose a resource unnecessarily, or introduce an expensive and avoidable network dependency.
Table of Contents
- What Amazon VPC Provides
- Core VPC Networking Components
- Internet and Private Connectivity
- VPC Security and Troubleshooting
- Production Design Example: Highly Available SaaS API
- Ready-to-Use Example: VPC with Terraform and Python
- Performance, Cost, and Production Operations
- Frequently Asked Questions
- Conclusion
What Amazon VPC Provides
Amazon VPC gives applications a configurable network environment within AWS. You define network boundaries and connectivity instead of relying on a shared, fixed network topology. AWS manages the underlying physical infrastructure, while you configure the logical networking resources.
Most applications do not communicate directly through public internet addresses. An API may need to access a private database, an internal cache, and AWS services such as S3 or Secrets Manager. A VPC provides the network environment in which those communication paths can be organized and controlled.
Common VPC Use Cases
A VPC is useful for many types of AWS workloads:
- Web applications: Keep application servers behind public load balancers.
- Databases: Restrict network access to authorized application resources.
- Microservices: Enable internal communication between services.
- Container platforms: Provide subnet addresses and network policies for ECS and EKS workloads.
- Serverless applications: Allow VPC-connected Lambda functions to reach private dependencies.
- Hybrid networking: Connect AWS workloads to offices or data centers.
- Environment isolation: Separate development, staging, and production networks.
A typical application places its public-facing load balancer in public subnets, application compute in private subnets, and databases in private subnets with more restrictive security groups.
VPCs, Regions, and Availability Zones
A VPC belongs to one AWS Region and can span multiple Availability Zones within that Region. A subnet, however, exists in exactly one Availability Zone.
For example, a VPC in us-west-2 might contain public and private subnets in two different Availability Zones. Both Zones share the same logical VPC while providing separate infrastructure failure domains.
VPCs do not automatically span Regions. Multi-Region applications require separate regional VPCs and an explicit strategy for cross-Region connectivity.
AWS accounts typically include a default VPC in each Region, but production deployments often use explicitly managed VPCs to control address allocation, routes, security, and environment separation.
Core VPC Networking Components
The three building blocks of a VPC are IP addressing, subnets, and routing. Their configuration determines where resources are placed and which destinations their network packets can reach.
Many application networking problems are not caused by an unavailable AWS service. They are caused by an unexpected route, missing IP address, overly restrictive security group, or incorrectly configured subnet.
CIDR Blocks and IP Addressing
When creating a VPC, you specify an IPv4 CIDR block. Classless Inter-Domain Routing (CIDR) notation represents an IP address range using a network address and prefix length.
For example:
VPC:
10.40.0.0/16
Public subnet A:
10.40.0.0/24
Public subnet B:
10.40.1.0/24
Private subnet A:
10.40.10.0/24
Private subnet B:
10.40.11.0/24
The VPC's /16 range contains 65,536 IPv4 addresses. Each /24 subnet contains 256 addresses, although AWS reserves five IPv4 addresses in every subnet, leaving 251 available for resources.
These subnet ranges must not overlap within the same VPC. They must also fit within the VPC's assigned address space.
Applications that may later connect to corporate networks, partner VPCs, or other AWS accounts should choose CIDR ranges carefully. Overlapping address spaces complicate routing and can prevent straightforward use of VPC peering or other connectivity mechanisms.
VPCs also support IPv6 configurations. In a dual-stack network, routing and security must be designed independently for IPv4 and IPv6 rather than assuming that IPv4 controls automatically cover both protocols.
Public, Private, and Isolated Subnets
The difference between public and private subnets is primarily their routing configuration.
- Public subnet: Has an applicable route to an internet gateway.
- Private subnet: Does not have a direct route to an internet gateway, but may reach external destinations through NAT or another network path.
- Isolated subnet: Has no general internet egress route and is intended for workloads that need only local or specifically configured connectivity.
A resource in a public subnet is not automatically accessible from the internet. For direct IPv4 internet access, it also needs an appropriate public IP address, security-group permissions, and any relevant network ACL permissions.
Conversely, a private subnet can reach the internet for outbound IPv4 requests through a suitable NAT gateway without exposing its resources to unsolicited inbound internet connections.
For example, an internet-facing Application Load Balancer can live in public subnets while its backend application tasks live in private subnets. The public entry point forwards requests to private IP addresses.
Route Tables and Longest Prefix Matching
A route table determines where packets travel based on their destination IP addresses. Each route contains a destination CIDR block and a target such as an internet gateway, NAT gateway, VPC endpoint, or network connection.
A public subnet's route table might contain:
Destination Target
---------------- -----------------
10.40.0.0/16 local
0.0.0.0/0 internet-gateway
A private subnet's route table might instead use:
Destination Target
---------------- -----------------
10.40.0.0/16 local
0.0.0.0/0 nat-gateway
S3 prefix list s3-gateway-endpoint
The local route supports communication within the VPC's address space. The default route 0.0.0.0/0 matches IPv4 destinations not covered by more specific routes.
Routing uses longest-prefix matching. A more specific route generally takes precedence over a broader route, subject to AWS route priority rules for cases involving different target types and route origins.
Every subnet is associated with one effective route table: either a custom table through explicit association or the VPC's main route table.
Internet and Private Connectivity
A VPC may need to communicate with the public internet, AWS services, other VPCs, or an on-premises data center. Different connectivity mechanisms exist because those destinations have different routing, security, and operational requirements.
Using an internet gateway for every connection is usually unnecessary and can introduce avoidable exposure or cost.
Internet Gateways and Public IP Addresses
An internet gateway enables internet connectivity for eligible resources inside a VPC. It is attached to the VPC, and public subnet route tables reference it for internet-bound traffic.
For direct IPv4 connectivity, an EC2 instance generally requires a public IPv4 address or Elastic IP, an appropriate route, and security permissions.
Attaching an internet gateway alone does not make resources public. Likewise, associating a public IP with a resource does not provide connectivity if its effective routes are incorrect.
Internet gateways support IPv6 connectivity as well. With globally routable IPv6 addresses, public access still depends on routing and security configuration.
NAT Gateways and Private Subnet Egress
A NAT gateway allows resources in private subnets to initiate outbound connections without assigning public IPv4 addresses directly to those resources.
For example, a private ECS task might need to download software dependencies or call an external payment API. Its subnet can route internet-bound traffic through a public NAT gateway.
The NAT gateway translates the source addresses for supported outbound connections and permits return traffic associated with those connections. It does not provide a public inbound endpoint to the private application.
For regional high availability, AWS generally recommends placing a public NAT gateway in each Availability Zone containing workloads that require outbound internet access.
Routing private workloads in both Zones through one NAT gateway is possible and cheaper for a demonstration, but it introduces an Availability Zone dependency and may generate cross-AZ data-transfer charges.
VPC Endpoints and AWS PrivateLink
VPC endpoints provide private connectivity to supported AWS services and endpoint services without requiring general internet access through a NAT gateway or internet gateway.
Two common endpoint types are:
- Gateway endpoints: Supported for Amazon S3 and DynamoDB, with route table integration.
- Interface endpoints: Create endpoint network interfaces with private IP addresses for supported services using AWS PrivateLink.
For example, an application in a private subnet can retrieve objects from S3 through an S3 gateway endpoint while its default internet route continues to use a NAT gateway for other destinations.
Gateway endpoints for S3 and DynamoDB do not incur hourly endpoint charges, while interface endpoints generally have hourly and data-processing charges.
VPC endpoints can reduce NAT traffic, strengthen network isolation, and enable workloads with no general internet access to reach supported AWS services. However, each required service dependency must have an appropriate network path.
VPC Peering, Transit Gateway, and Hybrid Networks
Applications sometimes need to communicate across multiple VPCs, AWS accounts, Regions, or corporate networks.
Common options include:
- VPC peering: Provides private IP connectivity between two compatible VPCs, subject to routing and nonoverlapping address requirements.
- Transit Gateway: Provides hub-and-spoke routing between multiple VPCs and supported external networks.
- Site-to-Site VPN: Creates encrypted connectivity between AWS and an external network.
- AWS Direct Connect: Provides dedicated network connectivity between customer locations and AWS.
- PrivateLink: Exposes supported application services privately without requiring broad network-level routing between entire VPCs.
VPC peering is not transitive. If VPC A peers with B and B peers with C, A cannot automatically communicate with C through B.
Large organizations often use Transit Gateway or a more deliberately segmented network topology to avoid managing a large mesh of individual peering relationships.
VPC Security and Troubleshooting
Routing determines where traffic can travel. Security controls determine whether that traffic is permitted. A correct route does not override a restrictive security group, and an open security group cannot create connectivity when no valid route exists.
Understanding both layers is essential when diagnosing connection timeouts between an API, database, or external service.
Security Groups vs. Network ACLs
Security groups and network access control lists (NACLs) both filter traffic, but they operate differently.
| Property | Security Group | Network ACL |
|---|---|---|
| Applied to | Network interfaces or associated resources | Subnet boundary |
| Stateful | Yes | No |
| Allow rules | Yes | Yes |
| Deny rules | No | Yes |
| Rule evaluation | Aggregated permitted traffic | Ordered rule numbers |
| Return traffic | State tracking permits associated responses | Explicit rules generally required |
Security groups are usually the primary control for application-to-database communication. For example, a PostgreSQL database security group can allow port 5432 from the application security group rather than from an entire network CIDR range.
Network ACLs provide an additional subnet-level boundary. Because they are stateless, restrictive rules must permit relevant return traffic, including ephemeral ports where appropriate.
Neither mechanism replaces application authentication, database authorization, TLS, or IAM permissions.
VPC Flow Logs and Reachability Analyzer
VPC Flow Logs capture metadata about IP traffic to and from supported network interfaces, subnets, or VPCs. They can help identify rejected connections, unexpected destinations, and traffic patterns.
Flow Logs are not full packet captures. They do not contain complete application payloads, and their delivery and coverage have documented limitations.
Reachability Analyzer examines network configuration to determine whether a modeled source-to-destination path is reachable. It can identify obstacles involving routes, security groups, NACLs, and other supported networking components.
A practical troubleshooting sequence is:
- Confirm the source and destination IP addresses.
- Inspect the source subnet's effective route table.
- Verify the destination's network interface and security groups.
- Check inbound and outbound NACL rules.
- Confirm DNS resolution and endpoint configuration.
- Inspect Flow Logs or use Reachability Analyzer.
- Test the application protocol and its authentication separately.
A successful network reachability check does not establish that a database user has valid credentials or that an application endpoint returns a correct response.
Production Design Example: Highly Available SaaS API
Consider a SaaS application using FastAPI on ECS Fargate, PostgreSQL on Amazon RDS, S3 for files, and a third-party payment provider. The API receives public HTTPS requests, while application tasks and databases remain on private IP addresses.
The design must tolerate the loss of an Availability Zone without exposing the database or relying on one cross-Zone internet egress component.
Three-Tier Network Architecture
A production VPC can use two or three Availability Zones with separate public, application, and data subnets.
- Public subnets: Host an internet-facing Application Load Balancer and public NAT gateways.
- Private application subnets: Host ECS tasks running the FastAPI API and workers.
- Private data subnets: Host RDS resources with restricted database access.
- Internet gateway: Provides internet connectivity for appropriately routed public resources.
- NAT gateways: Provide outbound IPv4 internet access for application workloads.
- VPC endpoints: Provide private connectivity to services such as S3, ECR, and Secrets Manager where configured.
- Security groups: Restrict ALB-to-API and API-to-database access.
The request path is:
Client
|
HTTPS
|
Public ALB
|
Private FastAPI tasks
|
Private RDS PostgreSQL
FastAPI tasks -- VPC endpoint --> S3
FastAPI tasks -- NAT gateway --> External payment API
The database should not require a public IP address. Its security group allows connections from approved application workloads on the database port.
The public ALB sends requests to private application targets. For additional details about load distribution and target availability, read What Is a Load Balancer?.
Failure Scenario: NAT Gateway Outage
Suppose application tasks run in two Availability Zones, but every private subnet routes outbound internet traffic through one public NAT gateway in Zone A.
If Zone A becomes unavailable, the API tasks in Zone B may still run and serve requests that do not require external internet access. However, requests to an external payment provider may fail because their configured egress path depends on the failed NAT gateway.
The application is partially available despite having healthy compute in another Zone. This illustrates why redundant containers do not automatically create a fully redundant network architecture.
For production, use zone-local egress paths appropriate to the chosen NAT gateway model and workload requirements. Route each private subnet to suitable egress infrastructure and test recovery under realistic Zone failures.
Also consider whether supported AWS API traffic can avoid NAT through VPC endpoints. This reduces unnecessary internet egress dependencies without removing the need for external connectivity where business operations require it.
Ready-to-Use Example: VPC with Terraform and Python
This example provisions a VPC with two public subnets, two private subnets, an internet gateway, one public NAT gateway, private route tables, an S3 gateway endpoint, and two security groups representing a public load balancer and a private application.
It then uses Python and boto3 to inspect the configured network. The Python script checks infrastructure configuration through AWS APIs; it does not pretend to prove actual packet connectivity without deploying a running compute resource.
1. Project Setup
Install Terraform, Python 3, and AWS CLI. Configure an AWS identity with the permissions required to provision and inspect VPC networking resources.
Create this project structure:
vpc-demo/
main.tf
outputs.tf
requirements.txt
audit_network.py
The example uses us-west-2 and two Availability Zones. It provisions a NAT gateway with an Elastic IP address, which incurs charges even if no application resources are running.
2. Provision Networking Infrastructure
Create main.tf:
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "us-west-2"
}
data "aws_availability_zones" "available" {
state = "available"
}
locals {
# Two subnets per tier, one in each AZ.
availability_zones = slice(
data.aws_availability_zones.available.names,
0,
2
)
}
# ------------------------------------
# VPC and subnets
# ------------------------------------
resource "aws_vpc" "main" {
cidr_block = "10.40.0.0/16"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "sysdespro-vpc-demo"
}
}
resource "aws_subnet" "public" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(
"10.40.0.0/16",
8,
count.index
)
availability_zone = local.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "public-${count.index + 1}"
Tier = "public"
}
}
resource "aws_subnet" "private" {
count = 2
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(
"10.40.0.0/16",
8,
count.index + 10
)
availability_zone = local.availability_zones[count.index]
map_public_ip_on_launch = false
tags = {
Name = "private-${count.index + 1}"
Tier = "private"
}
}
# ------------------------------------
# Internet gateway and public routes
# ------------------------------------
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "sysdespro-demo-igw"
}
}
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "public-route-table"
}
}
resource "aws_route_table_association" "public" {
count = 2
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
# ------------------------------------
# NAT gateway for private IPv4 egress
# ------------------------------------
resource "aws_eip" "nat" {
domain = "vpc"
tags = {
Name = "sysdespro-demo-nat-eip"
}
}
resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public[0].id
connectivity_type = "public"
depends_on = [
aws_internet_gateway.main
]
tags = {
Name = "sysdespro-demo-nat"
}
}
# Each private subnet uses its own route table.
# Both point to one NAT in this demo.
resource "aws_route_table" "private" {
count = 2
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
tags = {
Name = "private-route-table-${count.index + 1}"
}
}
resource "aws_route_table_association" "private" {
count = 2
subnet_id = aws_subnet.private[count.index].id
route_table_id = (
aws_route_table.private[count.index].id
)
}
# ------------------------------------
# Private connectivity to Amazon S3
# ------------------------------------
resource "aws_vpc_endpoint" "s3" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.us-west-2.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = concat(
[aws_route_table.public.id],
aws_route_table.private[*].id
)
tags = {
Name = "sysdespro-demo-s3-endpoint"
}
}
# ------------------------------------
# Application security groups
# ------------------------------------
resource "aws_security_group" "alb" {
name_prefix = "sysdespro-alb-"
description = "Ingress boundary for public ALB"
vpc_id = aws_vpc.main.id
}
resource "aws_vpc_security_group_ingress_rule" "https" {
security_group_id = aws_security_group.alb.id
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "tcp"
from_port = 443
to_port = 443
description = "Public HTTPS ingress"
}
resource "aws_vpc_security_group_egress_rule" "alb_to_app" {
security_group_id = aws_security_group.alb.id
referenced_security_group_id = aws_security_group.app.id
ip_protocol = "tcp"
from_port = 8000
to_port = 8000
description = "ALB to application"
}
resource "aws_security_group" "app" {
name_prefix = "sysdespro-app-"
description = "Private application service"
vpc_id = aws_vpc.main.id
}
resource "aws_vpc_security_group_ingress_rule" "app" {
security_group_id = aws_security_group.app.id
referenced_security_group_id = aws_security_group.alb.id
ip_protocol = "tcp"
from_port = 8000
to_port = 8000
description = "Only ALB can reach API port"
}
resource "aws_vpc_security_group_egress_rule" "app_https" {
security_group_id = aws_security_group.app.id
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "tcp"
from_port = 443
to_port = 443
description = "HTTPS egress to permitted destinations"
}
The configuration creates separate route tables for the two private subnets. This allows a production extension to route each Zone through independent egress infrastructure without restructuring the entire network.
The S3 gateway endpoint adds service-specific routing to the associated route tables. An S3 endpoint policy can further restrict resource access, although the demonstration uses the default endpoint permissions.
The security groups are prepared for an ALB and application service, but neither an ALB nor an application instance is provisioned by this example. HTTPS ingress on the ALB security group therefore does not expose any running application.
For production, also configure IPv6 intentionally if required, appropriate interface endpoints, VPC Flow Logs, restricted endpoint policies, and redundancy for external egress.
Create outputs.tf:
output "vpc_id" {
value = aws_vpc.main.id
}
output "public_subnet_ids" {
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
value = aws_subnet.private[*].id
}
output "nat_gateway_id" {
value = aws_nat_gateway.main.id
}
output "s3_endpoint_id" {
value = aws_vpc_endpoint.s3.id
}
output "alb_security_group_id" {
value = aws_security_group.alb.id
}
output "app_security_group_id" {
value = aws_security_group.app.id
}
3. Audit Networking with Python
The following script uses the EC2 API to inspect the created VPC, subnet configuration, effective route tables, and S3 endpoint.
Create requirements.txt:
boto3
Create audit_network.py:
import os
import boto3
REGION = os.getenv("AWS_REGION", "us-west-2")
VPC_ID = os.environ["VPC_ID"]
ec2 = boto3.client("ec2", region_name=REGION)
def get_route_tables() -> list[dict]:
response = ec2.describe_route_tables(
Filters=[
{
"Name": "vpc-id",
"Values": [VPC_ID],
}
]
)
return response["RouteTables"]
def find_effective_route_table(
subnet_id: str,
route_tables: list[dict],
) -> dict:
# Prefer an explicit subnet association.
for route_table in route_tables:
for association in route_table["Associations"]:
if association.get("SubnetId") == subnet_id:
return route_table
# Otherwise use the VPC main route table.
for route_table in route_tables:
for association in route_table["Associations"]:
if association.get("Main"):
return route_table
raise RuntimeError(
f"No route table found for {subnet_id}"
)
def route_target(route: dict) -> str:
# Route targets use different response fields.
for field in (
"GatewayId",
"NatGatewayId",
"TransitGatewayId",
"VpcPeeringConnectionId",
"NetworkInterfaceId",
"VpcEndpointId",
):
if route.get(field):
return route[field]
return "unknown"
def analyze_subnet(
subnet: dict,
route_tables: list[dict],
) -> None:
subnet_id = subnet["SubnetId"]
route_table = find_effective_route_table(
subnet_id,
route_tables,
)
routes = route_table["Routes"]
# An IPv4 default route to an IGW
# identifies an internet-routed subnet.
public_ipv4 = any(
route.get("DestinationCidrBlock") == "0.0.0.0/0"
and route.get("GatewayId", "").startswith("igw-")
and route.get("State") == "active"
for route in routes
)
nat_ipv4 = any(
route.get("DestinationCidrBlock") == "0.0.0.0/0"
and route.get("NatGatewayId")
and route.get("State") == "active"
for route in routes
)
print(
f"\nSubnet: {subnet_id}"
f"\nAZ: {subnet['AvailabilityZone']}"
f"\nCIDR: {subnet['CidrBlock']}"
f"\nAuto public IPv4: "
f"{subnet['MapPublicIpOnLaunch']}"
f"\nPublic IPv4 route: {public_ipv4}"
f"\nNAT IPv4 route: {nat_ipv4}"
)
for route in routes:
destination = (
route.get("DestinationCidrBlock")
or route.get("DestinationIpv6CidrBlock")
or route.get("DestinationPrefixListId")
or "unknown"
)
print(
f" {destination:24} "
f"-> {route_target(route)}"
)
def main() -> None:
vpcs = ec2.describe_vpcs(
VpcIds=[VPC_ID]
)["Vpcs"]
if not vpcs:
raise RuntimeError("VPC not found")
print("Auditing VPC:", VPC_ID)
print("CIDR:", vpcs[0]["CidrBlock"])
response = ec2.describe_subnets(
Filters=[
{
"Name": "vpc-id",
"Values": [VPC_ID],
}
]
)
subnets = response["Subnets"]
route_tables = get_route_tables()
for subnet in sorted(
subnets,
key=lambda item: item["CidrBlock"],
):
analyze_subnet(subnet, route_tables)
endpoints = ec2.describe_vpc_endpoints(
Filters=[
{
"Name": "vpc-id",
"Values": [VPC_ID],
}
]
)["VpcEndpoints"]
print("\nVPC endpoints:")
for endpoint in endpoints:
print(
endpoint["VpcEndpointId"],
endpoint["ServiceName"],
endpoint["VpcEndpointType"],
endpoint["State"],
)
if __name__ == "__main__":
main()
The script identifies the effective subnet route table even when the subnet uses the VPC's main route table implicitly. It also distinguishes a direct IPv4 internet gateway route from a NAT gateway route.
This is a configuration audit, not a complete connectivity test. Actual traffic still depends on security groups, NACLs, destination availability, DNS, and application protocols.
The script focuses on IPv4 internet routes. Dual-stack production auditing should also inspect IPv6 routes, egress-only internet gateways, and IPv6 security rules.
4. Deploy and Verify
Run Terraform to provision the network:
# Configure your AWS credentials first.
export AWS_REGION="us-west-2"
terraform init
terraform fmt
terraform validate
# Review the infrastructure changes.
terraform plan
# Create VPC networking resources.
terraform apply
# Export the VPC identifier.
export VPC_ID="$(terraform output -raw vpc_id)"
# Install boto3 in an isolated environment.
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Audit subnets, routes, and endpoints.
python audit_network.py
# Inspect the NAT gateway.
aws ec2 describe-nat-gateways \
--filter "Name=vpc-id,Values=$VPC_ID" \
--query "NatGateways[].{Id:NatGatewayId,State:State,Subnet:SubnetId}"
# Inspect S3 endpoint configuration.
aws ec2 describe-vpc-endpoints \
--filters "Name=vpc-id,Values=$VPC_ID" \
--query "VpcEndpoints[].{Id:VpcEndpointId,Service:ServiceName,Type:VpcEndpointType}"
The Python audit should report two public subnets with internet gateway routes and two private subnets with NAT gateway routes. It should also identify the S3 gateway endpoint.
To verify packet-level connectivity, deploy a temporary test instance or container in a private subnet with appropriate security permissions. Test an external HTTPS destination and an S3 operation separately, then inspect the relevant route tables and Flow Logs.
Do not interpret successful AWS API calls from your laptop as proof that resources inside the private subnets have working outbound connectivity. Your laptop's network path is different from the VPC resource's network path.
5. Clean Up
The demonstration includes one NAT gateway and one Elastic IP allocation, so remove unused infrastructure promptly.
# Review resources Terraform will delete.
terraform plan -destroy
# Remove the development infrastructure.
terraform destroy
# Verify that the demo VPC no longer exists.
aws ec2 describe-vpcs \
--filters "Name=tag:Name,Values=sysdespro-vpc-demo" \
--query "Vpcs[].VpcId"
# Verify NAT gateways associated with the VPC
# have completed their deletion lifecycle.
aws ec2 describe-nat-gateways \
--filter "Name=vpc-id,Values=$VPC_ID" \
--query "NatGateways[].{Id:NatGatewayId,State:State}"
Deletion can take time because AWS must release network interfaces and other dependencies before deleting the VPC. The NAT gateway may temporarily remain visible in a deleting or deleted state.
Do not destroy a VPC shared by production applications, databases, or other infrastructure. Real VPCs often contain resources managed by separate Terraform states, CloudFormation stacks, or AWS services.
Performance, Cost, and Production Operations
Amazon VPC itself generally has no additional hourly charge, but components and traffic associated with the network can generate significant costs. A small application may spend more on network egress and NAT gateways than on some of its application resources.
Operational monitoring should cover routing dependencies, address availability, rejected traffic, and the cost of moving data between network boundaries.
Networking Cost Drivers
Common VPC-related charges include:
- NAT gateways: Hourly usage and processed data.
- Public IPv4 addresses: Applicable hourly address charges.
- Interface endpoints: Hourly endpoint and data-processing costs.
- Cross-AZ data transfer: Applicable traffic between Availability Zones.
- Transit Gateway: Attachment and data-processing charges.
- VPN and Direct Connect: Connectivity and data-transfer charges.
- Flow Logs: Storage and processing costs at the selected destination.
- Load balancers: Regional load balancing charges.
An S3 gateway endpoint can be especially valuable for workloads that otherwise send substantial S3 traffic through a NAT gateway.
However, adding interface endpoints indiscriminately can increase fixed costs. Estimate expected data volume and required availability before choosing between endpoint connectivity and NAT-based access.
Common Mistakes
Frequent VPC design and configuration mistakes include:
- Assuming a public subnet gives every resource internet access: Public IP addresses and permissions still matter.
- Putting databases in public subnets unnecessarily: Increases exposure and complicates access control.
- Routing all Zones through one NAT gateway: Creates a cross-Zone failure dependency.
- Ignoring overlapping CIDR blocks: Makes inter-VPC and hybrid connectivity difficult.
- Opening database ports to 0.0.0.0/0: Allows broad network exposure.
- Confusing security groups and NACLs: Stateless return-traffic rules are omitted.
- Assuming private subnets have no outbound access: NAT and endpoints can still provide connectivity.
- Forgetting required interface endpoints: Private workloads fail to reach ECR or AWS APIs.
- Ignoring subnet IP exhaustion: New ECS tasks or EKS Pods cannot obtain network addresses.
- Leaving unrestricted default egress rules: Internal applications can access more destinations than intended.
- Not reviewing route propagation: VPN or Transit Gateway routing creates unexpected network paths.
- Assuming network isolation equals application authorization: Connected resources may still have excessive privileges.
Production Checklist
Before using a VPC for a critical application, verify that:
- CIDR ranges support future growth and do not conflict with connected networks.
- Public and private subnets span the required Availability Zones.
- Databases and internal services do not require public addresses.
- Route tables have the intended local, internet, NAT, and endpoint routes.
- External egress has suitable availability and capacity.
- Security groups allow only required application traffic.
- Network ACLs do not accidentally block return traffic.
- VPC endpoints and their policies are configured where appropriate.
- DNS resolution and hostname settings match workload requirements.
- IPv6 routing and security are explicitly reviewed when enabled.
- Subnet address capacity supports scaling and failover.
- Flow Logs and reachability diagnostics are available for troubleshooting.
- Cross-AZ traffic, NAT usage, endpoints, and public IPv4 costs are monitored.
- Network infrastructure is defined and reviewed through version-controlled configuration.
Frequently Asked Questions
Amazon VPC provides the network foundation for many AWS applications. The most common misunderstandings concern subnet exposure, regional scope, and whether private resources can access external services.
What Is Amazon VPC Used For?
Amazon VPC is used to create and manage logically isolated networks in AWS. It provides IP addressing, subnets, routing, gateways, security groups, and private connectivity for services such as EC2, ECS, EKS, and RDS.
Is Amazon VPC Free?
Creating and using a VPC has no additional VPC hourly charge. However, associated components such as NAT gateways, interface endpoints, public IPv4 addresses, and data transfer can incur charges.
The total network cost depends on the selected connectivity architecture and traffic patterns.
What Makes a Subnet Public?
A subnet is considered public when its associated route table has an applicable route to an internet gateway.
Individual resources still need appropriate public addresses and security permissions for direct internet connectivity.
Can a Private Subnet Access the Internet?
Yes. A private subnet can use a public NAT gateway or another appropriate egress architecture for outbound internet access without directly exposing its resources to unsolicited inbound connections.
It can also use VPC endpoints to reach supported AWS services without general internet access.
Can a VPC Span Multiple Availability Zones?
Yes. A VPC spans the Availability Zones in its AWS Region. Each subnet belongs to one Availability Zone, so highly available architectures commonly create corresponding subnets across multiple Zones.
A VPC itself does not extend across Regions; cross-Region networks require separately configured VPCs and connectivity.
Does a VPC Automatically Secure My Application?
No. A VPC provides network isolation and configurable security controls, but applications still require correct security groups, routes, IAM permissions, authentication, encryption, and monitoring.
A misconfigured public resource can remain exposed even when it exists inside a VPC.
Conclusion
Amazon VPC is the foundation of AWS networking. It allows engineers to define isolated network environments, place resources into subnets, control traffic using route tables, and connect workloads to the internet, AWS services, and private networks.
The most important design decisions are not simply choosing a CIDR range and creating two subnets. Production reliability depends on carefully planned address space, multiple Availability Zones, secure resource-to-resource access, resilient outbound connectivity, and visibility into network behavior.
Well-designed VPCs make applications easier to secure, scale, and operate. Poorly designed networks can create hidden single points of failure, unexpected exposure, and expensive data-transfer paths even when application services themselves are correctly configured.
Key Takeaway: Amazon VPC provides the networking foundation for AWS workloads, but secure and reliable connectivity is an architectural responsibility. Design subnets, routes, gateways, endpoints, and security boundaries around actual application traffic and failure scenarios.
More Articles to Read
Explore these additional SysDesPro articles for related guidance on AWS infrastructure, network-facing application architecture, and scalable cloud deployments.
Comments (0)