DEV Community

John  Ajera
John Ajera

Posted on

VPC Design and CIDR Planning

VPC Design & CIDR Planning

This article covers designing and creating an Amazon VPC with proper CIDR block selection and IP address allocation. We'll walk through the planning process and Terraform implementation.


CIDR Block Selection

Understanding CIDR Notation

CIDR (Classless Inter-Domain Routing) notation defines an IP address range using a prefix and subnet mask:

10.0.0.0/16
│   │   │ │
│   │   │ └─ Subnet mask (16 bits = /16)
│   │   └─── Network portion
│   └─────── Host portion
└─────────── IP address range
Enter fullscreen mode Exit fullscreen mode

Common CIDR blocks for VPCs:

  • /16 - 65,536 IP addresses (10.0.0.0 to 10.0.255.255)
  • /20 - 4,096 IP addresses (10.0.0.0 to 10.0.15.255)
  • /24 - 256 IP addresses (10.0.0.0 to 10.0.0.255)

Choosing the Right CIDR Block

Factors to consider:

  1. Current needs: How many resources do you need now?
  2. Future growth: Plan for 2-3x current capacity
  3. Subnet requirements: Each subnet needs its own IP range
  4. AWS reserved IPs: AWS reserves 5 IPs per subnet (first 4 + last 1)

Size recommendations:

  • Small projects: /24 (256 IPs) - development or small applications
  • Medium projects: /20 (4,096 IPs) - most production workloads
  • Large projects: /16 (65,536 IPs) - enterprise-scale deployments (recommended for production)

Private IP Ranges

Use RFC 1918 private IP address ranges:

  • 10.0.0.0/8 - 16,777,216 addresses (10.0.0.0 to 10.255.255.255)
  • 172.16.0.0/12 - 1,048,576 addresses (172.16.0.0 to 172.31.255.255)
  • 192.168.0.0/16 - 65,536 addresses (192.168.0.0 to 192.168.255.255)

Best practice: Start with 10.0.0.0/16 for most use cases.

Avoiding IP Conflicts

Important considerations:

  • Don't overlap with existing VPCs if you plan to peer them
  • Don't overlap with on-premises networks if using VPN/Direct Connect
  • Don't overlap with other AWS services you might connect to

Example conflict scenarios:

❌ VPC A: 10.0.0.0/16
❌ VPC B: 10.0.0.0/16  (Overlaps - cannot peer)

✅ VPC A: 10.0.0.0/16
✅ VPC B: 10.1.0.0/16  (No overlap - can peer)
Enter fullscreen mode Exit fullscreen mode

IP Address Allocation Strategy

Subnet Planning

When planning subnets, consider:

  1. Number of availability zones: Typically 2-3 AZs for high availability
  2. Public subnets: Need IPs for ALBs, NAT Gateways
  3. Private subnets: Need IPs for ECS tasks, databases, internal services
  4. Reserve IP ranges: Don't allocate all IPs immediately - leave room for growth

Example Allocation

For a /16 VPC (10.0.0.0/16) with 3 availability zones:

VPC: 10.0.0.0/16 (65,536 IPs total)

Availability Zone A:
  - Public Subnet:  10.0.1.0/24  (256 IPs)
  - Private Subnet: 10.0.11.0/20 (4,096 IPs)

Availability Zone B:
  - Public Subnet:  10.0.2.0/24  (256 IPs)
  - Private Subnet: 10.0.21.0/20 (4,096 IPs)

Availability Zone C:
  - Public Subnet:  10.0.3.0/24  (256 IPs)
  - Private Subnet: 10.0.31.0/20 (4,096 IPs)

Reserved for future:
  - 10.0.4.0/16 to 10.0.10.0/16
  - 10.0.12.0/16 to 10.0.20.0/16
  - etc.
Enter fullscreen mode Exit fullscreen mode

AWS Reserved IPs

AWS reserves 5 IP addresses per subnet:

  • First 4 IPs: Network address, VPC router, DNS server, reserved
  • Last 1 IP: Broadcast address

Example for 10.0.1.0/24:

  • 10.0.1.0 - Network address (reserved)
  • 10.0.1.1 - VPC router (reserved)
  • 10.0.1.2 - DNS server (reserved)
  • 10.0.1.3 - Reserved for future use
  • 10.0.1.4 to 10.0.1.254 - Available for use (251 IPs)
  • 10.0.1.255 - Broadcast address (reserved)

Terraform Implementation

Basic VPC Creation

resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "content-delivery-vpc"
  }
}
Enter fullscreen mode Exit fullscreen mode

Key parameters:

  • cidr_block: The primary CIDR block for your VPC
  • enable_dns_hostnames: Enables DNS hostnames for instances
  • enable_dns_support: Enables DNS resolution within the VPC

Using Variables for Flexibility

variable "vpc_cidr" {
  description = "CIDR block for VPC"
  type        = string
  default     = "10.0.0.0/16"
}

variable "availability_zones" {
  description = "Availability zones"
  type        = list(string)
  default     = ["ap-southeast-2a", "ap-southeast-2b"]
}

resource "aws_vpc" "main" {
  cidr_block           = var.vpc_cidr
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "content-delivery-vpc"
  }
}
Enter fullscreen mode Exit fullscreen mode

Subnet CIDR Calculation

locals {
  # Calculate subnet CIDRs
  public_subnet_cidrs = [
    cidrsubnet(var.vpc_cidr, 8, 1),  # 10.0.1.0/24
    cidrsubnet(var.vpc_cidr, 8, 2),  # 10.0.2.0/24
  ]

  private_subnet_cidrs = [
    cidrsubnet(var.vpc_cidr, 8, 11), # 10.0.11.0/24
    cidrsubnet(var.vpc_cidr, 8, 12), # 10.0.12.0/24
  ]
}

resource "aws_subnet" "public" {
  count = length(var.availability_zones)

  vpc_id            = aws_vpc.main.id
  cidr_block        = local.public_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name = "public-subnet-${count.index + 1}"
  }
}

resource "aws_subnet" "private" {
  count = length(var.availability_zones)

  vpc_id            = aws_vpc.main.id
  cidr_block        = local.private_subnet_cidrs[count.index]
  availability_zone = var.availability_zones[count.index]

  tags = {
    Name = "private-subnet-${count.index + 1}"
  }
}
Enter fullscreen mode Exit fullscreen mode

Understanding cidrsubnet function:

cidrsubnet(prefix, newbits, netnum)
Enter fullscreen mode Exit fullscreen mode
  • prefix: Base CIDR block (e.g., "10.0.0.0/16")
  • newbits: Additional bits for subnet mask (8 = /24)
  • netnum: Subnet number (0, 1, 2, etc.)

Example:

cidrsubnet("10.0.0.0/16", 8, 1)  # Returns "10.0.1.0/24"
cidrsubnet("10.0.0.0/16", 8, 2)  # Returns "10.0.2.0/24"
cidrsubnet("10.0.0.0/16", 4, 11) # Returns "10.0.11.0/20"
Enter fullscreen mode Exit fullscreen mode

Design Best Practices

1. Use Consistent Numbering

Recommended pattern:

  • Public subnets: 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24
  • Private subnets: 10.0.11.0/24, 10.0.12.0/24, 10.0.13.0/24
  • Reserved ranges: 10.0.4.0/16 to 10.0.10.0/16 for future use

This makes IP allocation predictable and easier to manage.

2. Plan for Multiple Environments

Separate VPCs per environment:

  • Development: 10.1.0.0/16
  • Staging: 10.2.0.0/16
  • Production: 10.0.0.0/16

This prevents conflicts and allows for VPC peering if needed.

3. Document Your Allocation

Keep a record of your IP allocation for reference and troubleshooting.


Common Mistakes to Avoid

1. Too Small CIDR Block

Don't use /28 or smaller - You'll run out of IPs quickly

Use /24 minimum for subnets, /16 for VPC

2. Overlapping Subnets

Don't create overlapping subnets:

subnet1 = "10.0.1.0/24"  # 10.0.1.0 to 10.0.1.255
subnet2 = "10.0.1.128/25" # 10.0.1.128 to 10.0.1.255 (OVERLAPS!)
Enter fullscreen mode Exit fullscreen mode

Use non-overlapping ranges:

subnet1 = "10.0.1.0/24"   # 10.0.1.0 to 10.0.1.255
subnet2 = "10.0.2.0/24"   # 10.0.2.0 to 10.0.2.255 (No overlap)
Enter fullscreen mode Exit fullscreen mode

3. Not Planning for Growth

Don't allocate all IPs immediately - You'll need to recreate subnets later

Reserve IP ranges for future expansion - Leave gaps in your numbering scheme

4. Ignoring AWS Reserved IPs

Don't assume all IPs in a subnet are available

Account for 5 reserved IPs per subnet in your calculations


What's Next?

In the upcoming articles, we'll dive deeper into:

  • Subnet Architecture - Subnet configuration, route tables, and network ACLs implementation
  • NAT Gateways & Internet Gateways - Internet Gateway and NAT Gateway setup, placement strategies, and cost optimization
  • VPC Flow Logs & Network Monitoring - Flow log configuration, log analysis, and network troubleshooting

Repositories:

Top comments (0)