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:

  1. Environments are truly independent: Production and staging never need to communicate
  2. You use Infrastructure as Code: Resources are deployed, not migrated
  3. You have clear boundaries: No hybrid scenarios where environments blur together
  4. 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.