I’ve been designing VPC architectures in AWS, conventional wisdom tells us to avoid overlapping CIDR blocks. Heck, every single company I’ve worked at has an infrastructure or network team beholden to this idea. The reasoning is straightforward: non-overlapping CIDRs allow you to connect VPCs via peering, Transit Gateway, or other networking constructs if needed later.
But what if preventing that connection is exactly what you want? I have been given side eye for even suggesting this practice before, but hear me out.
The Context: Production vs Staging
Consider production and staging environments. These should be fundamentally isolated. They serve different purposes, have different security postures, and should never share network connectivity. Yet the standard guidance to use non-overlapping CIDRs keeps that door open—just in case.
This is where the Cynefin framework’s distinction between “best practices” and “good practices” becomes relevant. A best practice applies broadly across contexts with predictable results. A good practice is contextual, nuanced, and requires understanding the specific situation at hand.
Using non-overlapping CIDRs everywhere is a best practice for general VPC design. Using overlapping CIDRs between production and staging is a good practice for enforcing environment isolation.
The Architectural Decision
Use overlapping CIDR blocks between production and staging environments to create an immutable network boundary.
For example:
- Production VPC:
10.0.0.0/16 - Staging VPC:
10.0.0.0/16
graph TB
subgraph Production["Production VPC<br/>10.0.0.0/16"]
P1[Public Subnet<br/>10.0.1.0/24]
P2[Private Subnet<br/>10.0.2.0/24]
P3[Database Subnet<br/>10.0.3.0/24]
end
subgraph Staging["Staging VPC<br/>10.0.0.0/16"]
S1[Public Subnet<br/>10.0.1.0/24]
S2[Private Subnet<br/>10.0.2.0/24]
S3[Database Subnet<br/>10.0.3.0/24]
end
X[❌ Cannot peer or connect]
Production -.->|Overlapping CIDRs| X
Staging -.->|Overlapping CIDRs| X
style X fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px
style Production fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style Staging fill:#fff3e0,stroke:#f57c00,stroke-width:2px
Why This Works
1. Technical Constraint as Security Control
Overlapping CIDRs make it technically impossible to:
- Create VPC peering connections
- Attach both VPCs to the same Transit Gateway
- Use AWS PrivateLink between environments
- Route traffic between the environments
This isn’t a policy that can be misconfigured or bypassed—it’s a hard networking constraint.
2. Eliminates Anti-Patterns
By preventing network connectivity, you eliminate several operational anti-patterns:
The “Quick Copy” Temptation
graph LR
A[Production Database] -->|❌ Can't do this| B[Staging via VPC Peering]
style A fill:#e3f2fd,stroke:#1976d2
style B fill:#fff3e0,stroke:#f57c00
Teams can’t take shortcuts by peering VPCs to copy data or test connectivity. They must use proper mechanisms like database snapshots, S3 exports, or data anonymization pipelines.
The “Emergency Hotfix” Route
No one can route production traffic through staging “just this once” during an incident. The network topology enforces the boundary.
The “Shared Service” Mistake
You can’t accidentally create dependencies where staging relies on production services or vice versa through direct network paths.
3. Forces Proper Architecture
With overlapping CIDRs, you must design for proper separation from the start:
- Logging and Monitoring: Deploy separately in each environment or use agent-based collection
- Secrets Management: Use AWS Secrets Manager or Parameter Store independently per environment
- DNS: Separate Route53 zones or distinct namespaces
- CI/CD: Deploy from artifacts, never by moving resources between environments
When to Apply This Good Practice
This approach makes sense when:
- Environments are truly independent: Production and staging never need to communicate
- You use Infrastructure as Code: Resources are deployed, not migrated
- You have clear boundaries: No hybrid scenarios where environments blur together
- Security posture differs: Production has stricter controls than staging
When to Use Non-Overlapping CIDRs
Keep CIDRs unique when:
- Connecting regions within the same environment (prod us-west-2 ↔ prod ca-central-1)
- Linking infrastructure environments to workload environments (tooling → production for CI/CD)
- Building multi-account architectures with centralized networking
- Creating hub-and-spoke topologies with Transit Gateway
graph TB
subgraph Same Environment
PW[Production US-West-2<br/>10.0.0.0/16]
PC[Production CA-Central-1<br/>10.1.0.0/16]
end
subgraph Cross-Class
T[Tooling<br/>10.10.0.0/16]
P[Production<br/>10.0.0.0/16]
end
PW <-->|✓ Different CIDRs| PC
T <-->|✓ Different CIDRs| P
style PW fill:#e3f2fd,stroke:#1976d2
style PC fill:#e3f2fd,stroke:#1976d2
style T fill:#f3e5f5,stroke:#7b1fa2
style P fill:#e3f2fd,stroke:#1976d2
The Cynefin Lens
In the Cynefin framework, best practices work in the “obvious” domain where cause and effect are clear and solutions are repeatable. “Non-overlapping CIDRs” is a best practice for general VPC connectivity.
Good practices exist in the “complicated” domain where expertise and analysis are required. The relationship between cause and effect is understandable but not immediately obvious to everyone.
Using overlapping CIDRs between prod and staging is deemed a good practice because:
- It requires understanding your specific context
- The benefits depend on your deployment model and security requirements
- It trades one capability (connectivity) for another (immutable boundaries)
- It’s not universally applicable—it’s situational
Implementation Example
# CloudFormation template
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Environment-specific VPC with overlapping CIDR'
Parameters:
Environment:
Type: String
Default: production
AllowedValues:
- production
- staging
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16 # Same CIDR for both environments
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub '${Environment}-vpc'
- Key: Environment
Value: !Ref Environment
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.1.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${Environment}-public-subnet'
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.2.0/24
Tags:
- Key: Name
Value: !Sub '${Environment}-private-subnet'
DatabaseSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.0.3.0/24
Tags:
- Key: Name
Value: !Sub '${Environment}-database-subnet'
# Deploy this same template in different accounts/regions
# with Environment parameter set to 'production' or 'staging'
Make the Possible, Impossible
Overlapping CIDRs between production and staging isn’t a mistake—it’s an architectural decision that uses network topology to enforce environment boundaries. It prevents an entire class of connectivity anti-patterns while forcing teams to adopt proper deployment and data management practices.
This isn’t the right choice for every organization or every scenario. But when you’ve decided that production and staging should never communicate over the network, overlapping CIDRs transforms that policy decision into an immutable technical constraint.
Sometimes the best way to prevent a connection is to make it impossible.