AWS VPC — Virtual Private Cloud Full Walkthrough
A full walkthrough of AWS VPC — subnets, route tables, internet gateways, NAT, security groups, NACLs, flow logs, endpoints, and peering
What is a VPC?
A Virtual Private Cloud (VPC) is your own logically isolated network inside AWS. Think of it as if AWS gave you an empty floor of a data centre and said “design your own network from scratch.” Every resource you launch — EC2 instances, RDS databases, Lambda functions — goes inside a VPC. You control the IP address ranges, subnets, routing, and what can reach the internet and what cannot.
When you create an AWS account, a default VPC is automatically created in every region. The default VPC has a CIDR of 172.31.0.0/16 and a public subnet in each AZ. It is meant for quick testing — production workloads should always use a custom VPC.
CIDR Blocks
A VPC is defined by its CIDR block — the range of private IP addresses available inside it.
1
2
3
4
10.0.0.0/16 → 65,536 IP addresses (recommended for production)
10.0.0.0/24 → 256 IP addresses
192.168.1.0/24 → 256 IP addresses
172.16.0.0/12 → 1,048,576 IP addresses
AWS reserves 5 IP addresses from every subnet — the first 4 and the last 1. For a /24 subnet (256 IPs), you get 251 usable addresses.
Private IP ranges (RFC 1918) — use these inside your VPC:
10.0.0.0/8172.16.0.0/12192.168.0.0/16
Creating a VPC
In the Console
📸 SCREENSHOT: AWS Console → VPC → Your VPCs → Create VPC. Show the form with Name tag, IPv4 CIDR block field, and the “VPC only” vs “VPC and more” option selected.
Go to VPC → Your VPCs → Create VPC.
Fill in:
- Name tag:
prod-vpc - IPv4 CIDR:
10.0.0.0/16 - Tenancy: Default (Dedicated tenancy means your instances run on hardware not shared with other customers — much more expensive)
With the AWS CLI
1
2
3
4
5
6
7
8
9
10
# Create the VPC
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications \
'ResourceType=vpc,Tags=[{Key=Name,Value=prod-vpc}]'
# List your VPCs
aws ec2 describe-vpcs --query 'Vpcs[*].[VpcId,CidrBlock,Tags[0].Value]' --output table
# Enable DNS hostnames (required for some services like EKS)
aws ec2 modify-vpc-attribute --vpc-id vpc-0abc123 --enable-dns-hostnames
aws ec2 modify-vpc-attribute --vpc-id vpc-0abc123 --enable-dns-support
Subnets
A subnet is a range of IP addresses within your VPC, tied to a single Availability Zone. Subnets are how you separate public-facing resources from private ones.
Public vs Private Subnets
| Public Subnet | Private Subnet | |
|---|---|---|
| Route to internet | Via Internet Gateway | Via NAT Gateway (outbound only) |
| Resources inside | Load balancers, bastion hosts | EC2 app servers, databases |
| Direct inbound traffic | Yes | No |
| Has public IPs | Yes (if auto-assign enabled) | No |
Recommended subnet layout
For a VPC 10.0.0.0/16 across 3 AZs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Public subnets (for ALB, NAT GW, bastion)
10.0.1.0/24 → AZ-a
10.0.2.0/24 → AZ-b
10.0.3.0/24 → AZ-c
Private subnets (for EC2, EKS nodes, app layer)
10.0.11.0/24 → AZ-a
10.0.12.0/24 → AZ-b
10.0.13.0/24 → AZ-c
Database subnets (for RDS, ElastiCache)
10.0.21.0/24 → AZ-a
10.0.22.0/24 → AZ-b
10.0.23.0/24 → AZ-c
Three tiers, three AZs, nine subnets. This gives you fault tolerance and clear separation of concerns.
Creating subnets
📸 SCREENSHOT: VPC → Subnets → Create Subnet. Show the subnet creation form with VPC ID selected, subnet name, AZ dropdown, and CIDR block field.
1
2
3
4
5
6
7
8
9
10
11
# Create a public subnet in AZ-a
aws ec2 create-subnet \
--vpc-id vpc-0abc123 \
--cidr-block 10.0.1.0/24 \
--availability-zone eu-west-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=public-1a}]'
# Enable auto-assign public IPs for public subnets
aws ec2 modify-subnet-attribute \
--subnet-id subnet-0abc123 \
--map-public-ip-on-launch
Internet Gateway (IGW)
An Internet Gateway is the door between your VPC and the public internet. It is horizontally scaled, redundant, and managed by AWS — you never have to worry about it failing. A VPC can only have one IGW attached at a time. Without an IGW, nothing inside your VPC can send or receive traffic from the internet, even if it has a public IP.
📸 SCREENSHOT: VPC → Internet Gateways → Create Internet Gateway, then the “Attach to VPC” action shown in the Actions dropdown.
1
2
3
4
5
6
7
8
# Create the IGW
aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=prod-igw}]'
# Attach it to your VPC
aws ec2 attach-internet-gateway \
--internet-gateway-id igw-0abc123 \
--vpc-id vpc-0abc123
Route Tables
A route table is a set of rules that determines where network traffic is directed. Every subnet must be associated with a route table. If no explicit association exists, the subnet uses the VPC’s main route table.
How routing works
Each entry in a route table has:
- Destination — the CIDR range this rule applies to
- Target — where to send the traffic
1
2
3
4
5
6
7
8
9
10
Public subnet route table:
10.0.0.0/16 → local (traffic within the VPC stays local)
0.0.0.0/0 → igw-0abc123 (everything else goes to the internet)
Private subnet route table:
10.0.0.0/16 → local
0.0.0.0/0 → nat-0abc123 (outbound internet via NAT)
Database subnet route table:
10.0.0.0/16 → local (no internet access at all)
📸 SCREENSHOT: VPC → Route Tables → select a public route table → Routes tab. Show the two routes: the local route and the 0.0.0.0/0 → IGW route.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Create a route table
aws ec2 create-route-table --vpc-id vpc-0abc123 \
--tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=public-rt}]'
# Add a route to the internet via IGW
aws ec2 create-route \
--route-table-id rtb-0abc123 \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id igw-0abc123
# Associate the route table with a subnet
aws ec2 associate-route-table \
--route-table-id rtb-0abc123 \
--subnet-id subnet-0abc123
NAT Gateway
A NAT Gateway (Network Address Translation) lets instances in a private subnet make outbound connections to the internet, while blocking inbound connections from the internet.
Use case: your app server in a private subnet needs to pull packages from the internet or call an external API. It sends traffic to the NAT Gateway in the public subnet. The NAT Gateway forwards it to the internet using its own Elastic IP. The response comes back to the NAT Gateway, which forwards it back to your private instance. No one on the internet can initiate a connection to your private instance.
NAT Gateway vs NAT Instance
| NAT Gateway | NAT Instance | |
|---|---|---|
| Managed by | AWS | You |
| Availability | Highly available per AZ | Single point of failure |
| Bandwidth | Up to 100 Gbps | Limited by instance size |
| Cost | Higher | Lower |
| Recommendation | Always use for production | Legacy / cost-cutting only |
Setup
📸 SCREENSHOT: VPC → NAT Gateways → Create NAT Gateway. Show the form with the public subnet selected, Elastic IP allocation button, and connectivity type set to Public.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Allocate an Elastic IP for the NAT Gateway
aws ec2 allocate-address --domain vpc
# Create NAT Gateway in a PUBLIC subnet (not private!)
aws ec2 create-nat-gateway \
--subnet-id subnet-public-1a \
--allocation-id eipalloc-0abc123 \
--tag-specifications 'ResourceType=natgateway,Tags=[{Key=Name,Value=nat-1a}]'
# Add the NAT Gateway route to your private subnet route table
aws ec2 create-route \
--route-table-id rtb-private \
--destination-cidr-block 0.0.0.0/0 \
--nat-gateway-id nat-0abc123
One NAT Gateway per AZ — if you have 3 AZs, create 3 NAT Gateways. A single NAT Gateway in one AZ means your private subnets in other AZs lose internet access if that AZ fails.
Security Groups
A Security Group is a virtual firewall attached to a network interface (ENI). It is stateful — if you allow inbound traffic, the response is automatically allowed outbound. Security Groups only have Allow rules — you cannot explicitly deny. All traffic is denied by default until you add an allow rule.
Rules
1
2
3
4
5
6
7
8
Inbound rules:
Type Protocol Port Source
SSH TCP 22 10.0.1.0/24 (only from bastion subnet)
HTTPS TCP 443 0.0.0.0/0 (public web traffic)
Custom TCP 8080 sg-0abc123 (from ALB security group)
Outbound rules:
All traffic All All 0.0.0.0/0 (default — allow all outbound)
Security Group referencing
Instead of hardcoding IP addresses, you can reference another security group as the source. This is the recommended pattern — your EC2 instances only accept traffic from the ALB’s security group, not from the entire world.
📸 SCREENSHOT: EC2 → Security Groups → select a security group → Inbound Rules tab. Show a rule where the Source is another security group ID (e.g. the ALB SG), not an IP range.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Create a security group
aws ec2 create-security-group \
--group-name app-sg \
--description "App server security group" \
--vpc-id vpc-0abc123
# Allow HTTPS from anywhere
aws ec2 authorize-security-group-ingress \
--group-id sg-0abc123 \
--protocol tcp \
--port 443 \
--cidr 0.0.0.0/0
# Allow port 8080 only from the ALB security group
aws ec2 authorize-security-group-ingress \
--group-id sg-app \
--protocol tcp \
--port 8080 \
--source-group sg-alb
Network ACLs (NACLs)
A Network ACL is a stateless firewall that applies at the subnet level. Stateless means you must explicitly allow both inbound and return outbound traffic. NACLs have both Allow and Deny rules, evaluated in order by rule number (lowest first).
Security Group vs NACL
| Security Group | NACL | |
|---|---|---|
| Level | Instance (ENI) | Subnet |
| State | Stateful | Stateless |
| Rules | Allow only | Allow and Deny |
| Default | Deny all inbound | Allow all |
| Evaluation | All rules | Rules in number order |
| Use case | Primary access control | Extra layer, blocking specific IPs |
NACLs are most useful for blocking specific IP addresses (e.g. a known attacker IP) at the subnet boundary. For most use cases, Security Groups are sufficient.
📸 SCREENSHOT: VPC → Network ACLs → select an ACL → Inbound Rules tab. Show the numbered rules list with Allow/Deny column and the asterisk (*) deny-all rule at the bottom.
VPC Flow Logs
VPC Flow Logs capture metadata about IP traffic going in and out of network interfaces in your VPC. They do not capture the actual packet contents — just the metadata (source IP, destination IP, port, protocol, action, bytes).
Flow logs are essential for:
- Security investigations — who connected to what
- Troubleshooting connectivity — why is traffic being rejected
- Compliance — evidence that traffic controls are working
Flow log record format
1
2
3
4
version account-id interface-id srcaddr dstaddr srcport dstport protocol packets bytes start end action log-status
2 123456789012 eni-0abc123 10.0.1.5 10.0.2.10 54321 443 6 20 4000 1620000000 1620000060 ACCEPT OK
2 123456789012 eni-0abc123 1.2.3.4 10.0.1.5 80 443 6 5 300 1620000000 1620000060 REJECT OK
ACCEPT means the traffic was allowed by Security Group or NACL. REJECT means it was blocked.
Enabling flow logs
📸 SCREENSHOT: VPC → Your VPCs → select your VPC → Flow Logs tab → Create flow log. Show the destination options (CloudWatch Logs vs S3) and the IAM role field.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Send flow logs to CloudWatch Logs
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-0abc123 \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name /aws/vpc/flowlogs \
--deliver-logs-permission-arn arn:aws:iam::123456789012:role/FlowLogsRole
# Send to S3 (cheaper for long-term storage)
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-0abc123 \
--traffic-type ALL \
--log-destination-type s3 \
--log-destination arn:aws:s3:::my-flow-logs-bucket/vpc/
Traffic type options: ALL, ACCEPT, REJECT
VPC Endpoints
A VPC Endpoint lets your VPC communicate with AWS services (S3, DynamoDB, SSM, etc.) without going through the internet. Traffic stays on the AWS private backbone — no NAT Gateway required, no data transfer charges to the internet.
Interface Endpoint vs Gateway Endpoint
| Type | Services | How it works |
|---|---|---|
| Gateway Endpoint | S3, DynamoDB only | Adds a route in your route table |
| Interface Endpoint | Most other AWS services | Creates an ENI with a private IP in your subnet |
Gateway Endpoints are free. Interface Endpoints cost money per hour + per GB processed.
Creating a Gateway Endpoint for S3
📸 SCREENSHOT: VPC → Endpoints → Create Endpoint. Show the service name search with “com.amazonaws.eu-west-1.s3” selected, Gateway type selected, and the VPC and route table checkboxes.
1
2
3
4
5
# Create an S3 Gateway Endpoint
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0abc123 \
--service-name com.amazonaws.eu-west-1.s3 \
--route-table-ids rtb-private-1a rtb-private-1b rtb-private-1c
After this, your private EC2 instances can access S3 without a NAT Gateway.
VPC Peering
VPC Peering connects two VPCs so instances in each can communicate using private IPs. Peering can be within the same account, across accounts, and across regions (inter-region peering). Traffic goes over the AWS private backbone — not the internet.
Limitations
- No transitive peering — if VPC-A peers with VPC-B and VPC-B peers with VPC-C, VPC-A cannot reach VPC-C through VPC-B. Each pair of VPCs that needs to communicate must have their own peering connection.
- CIDR blocks cannot overlap.
📸 SCREENSHOT: VPC → Peering Connections → Create Peering Connection. Show the requester VPC and accepter VPC fields, and then the Pending Acceptance state before the other account accepts.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Request a peering connection
aws ec2 create-vpc-peering-connection \
--vpc-id vpc-0abc123 \
--peer-vpc-id vpc-0def456 \
--peer-region eu-west-2
# Accept it (run in the peer account/region)
aws ec2 accept-vpc-peering-connection \
--vpc-peering-connection-id pcx-0abc123
# Add routes in both VPCs pointing to the peering connection
aws ec2 create-route \
--route-table-id rtb-0abc123 \
--destination-cidr-block 10.1.0.0/16 \
--vpc-peering-connection-id pcx-0abc123
For large-scale connectivity between many VPCs, use AWS Transit Gateway instead of a mesh of peering connections.
Complete VPC Architecture Summary
1
2
3
4
5
6
7
8
9
10
11
12
13
14
VPC 10.0.0.0/16
│
├── Public Subnets (10.0.1-3.0/24)
│ ├── Internet Gateway attached
│ ├── Route: 0.0.0.0/0 → IGW
│ └── Resources: ALB, NAT Gateway, Bastion Host
│
├── Private Subnets (10.0.11-13.0/24)
│ ├── Route: 0.0.0.0/0 → NAT Gateway
│ └── Resources: EC2 app servers, EKS nodes
│
└── Database Subnets (10.0.21-23.0/24)
├── Route: local only (no internet)
└── Resources: RDS, ElastiCache
📸 SCREENSHOT: VPC → Resource Map tab (available in the console). This shows a visual diagram of your VPC with all subnets, route tables, and gateways connected — great for documentation.
Quick Reference
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# VPC
aws ec2 describe-vpcs --output table
aws ec2 create-vpc --cidr-block 10.0.0.0/16
# Subnets
aws ec2 describe-subnets --filters "Name=vpc-id,Values=vpc-0abc123" --output table
aws ec2 create-subnet --vpc-id vpc-0abc123 --cidr-block 10.0.1.0/24 --availability-zone eu-west-1a
# Internet Gateway
aws ec2 describe-internet-gateways --output table
aws ec2 attach-internet-gateway --internet-gateway-id igw-0abc123 --vpc-id vpc-0abc123
# NAT Gateway
aws ec2 describe-nat-gateways --output table
# Route Tables
aws ec2 describe-route-tables --filters "Name=vpc-id,Values=vpc-0abc123" --output table
# Security Groups
aws ec2 describe-security-groups --filters "Name=vpc-id,Values=vpc-0abc123" --output table
# Flow Logs
aws ec2 describe-flow-logs --output table
# Endpoints
aws ec2 describe-vpc-endpoints --output table
# Peering
aws ec2 describe-vpc-peering-connections --output table

