AWS Overlapping CIDR Fix: Transit Gateway 1:1 NAT Setup, Why Both VPCs Need a Rule, the Route It Replaces, VPN and Direct Connect, Cloud WAN, Pricing and Every Limit

Logeshwaran
—

Since October 23, 2026, AWS Transit Gateway and AWS Cloud WAN can connect networks whose CIDRs overlap, without renumbering and without a NAT appliance. You add 1:1 NAT rules to an attachment, and the transit gateway rewrites addresses in both directions: VPC-A's 10.0.0.0/16 becomes 100.64.0.0/16 on the way out, and replies come back to the real address on their own. One command sets it up: aws ec2 modify-transit-gateway-attachment-nat-configuration. The part almost everyone gets wrong comes next. If two VPCs both use 10.0.0.0/16, translating only one of them does not work. The untranslated VPC sends its replies to an address inside its own range, and its route table keeps that traffic local, so the replies never reach the transit gateway. You have to give each overlapping network its own NAT range. The second surprise: the moment you save a rule on a VPC attachment, the transit gateway replaces that VPC's real-address route with the NAT one, and anything that reached the VPC by its real addresses loses the path. This guide covers setup, routing, VPN and Direct Connect, Cloud WAN, pricing, limits and the traps.

Jake bought a second repair shop in October. The deal was simple; the network was not. The other shop's booking system runs in its own AWS account, in a VPC on 10.0.0.0/16, and Jake's inventory system sits in a VPC on exactly the same range. "So the two apps can't talk because somebody picked the same number?" Jake asked. "Two somebodies," Ethan said. "Last week the answer was a month of renumbering. This week it is four rules on a transit gateway, if you put them on both sides."

⚡ Quick Answer

• What it is: static, stateless 1:1 NAT on Transit Gateway and Cloud WAN attachments (a Regional private NAT gateway built into the transit layer). IPv4 only. How it works.

• Overlapping VPCs: give both VPCs a rule, each to its own NAT CIDR (for example 100.64.0.0/16 and 100.65.0.0/16). One-sided NAT fails. The trap.

• Command: aws ec2 modify-transit-gateway-attachment-nat-configuration --nat-gateway-support enable --nat-gateway-rules .... Setup.

• Rules: same prefix length on both sides, NAT CIDR in RFC 1918 or 100.64.0.0/10, up to 50 rules per attachment. Rule limits.

• Cost: billed at Regional NAT gateway rates (an hourly charge plus a per-GB charge) on top of normal Transit Gateway charges. Pricing.

Routing · VPN and Direct Connect · DNS · Cloud WAN · Limits · Troubleshooting · FAQ

New to AWS networking? A VPC is your own private network inside AWS, and a CIDR such as 10.0.0.0/16 is the block of addresses it uses; our plain-English VPC guide covers both. A transit gateway is a hub that connects many VPCs, VPNs and Direct Connect links through one set of route tables. If routing on a transit gateway is new to you, read our Transit Gateway routing guide first, because 1:1 NAT changes which routes appear in those tables.

🧭 NEW HERE? READ THESE FIRST

New to VPCs and Transit Gateway? Start with these five pages:

💡 Write down every CIDR you own before you write a single NAT rule. Most 1:1 NAT failures come from a range that somebody forgot was already in use.

What is Transit Gateway 1:1 NAT?

1:1 NAT is a mapping from one CIDR block to another of the same size, set on a single attachment. Every address keeps its position: if 10.10.100.0/24 maps to 100.64.100.0/24, then 10.10.100.5 always becomes 100.64.100.5. Only the network part changes; the host part (here the last number) stays the same, and ports are never touched.

Each rule works in both directions, so you never write a separate return rule:

  • Source NAT (SNAT): traffic arriving from the attachment with a source address in the original CIDR leaves the transit gateway with the matching NAT address.
  • Destination NAT (DNAT): traffic sent to the attachment with a destination address in the NAT CIDR is rewritten back to the original address before delivery.

Because the mapping is fixed, translation needs no connection table. Replies are translated without any state, and other networks can start connections to the NAT addresses too, which ordinary NAT gateways cannot do. That makes it useful for three jobs:

  • Overlapping addresses after a merger or acquisition, with a partner network over Site-to-Site VPN, with another cloud, or between VPCs that different teams numbered on their own.
  • Hiding your real addressing from partners or other business units, who only ever see the NAT range.
  • Running out of RFC 1918 space, by using the shared address space 100.64.0.0/10 (RFC 6598, the range carriers use for CGNAT) as NAT ranges.

Here is what it supports on October 24, 2026:

ItemTransit Gateway 1:1 NAT
LaunchedOctober 23, 2026, in every commercial Region where Transit Gateway and Cloud WAN run
Attachments with NATVPC, Direct Connect gateway, Site-to-Site VPN (including Private IP VPN), Connect, intra-Region and inter-Region peering, VPN Concentrator
No NAT onClient VPN, Network Function attachments, Cloud WAN peering and route table attachments
RulesUp to 50 per attachment; rule numbers 1 to 32766; no priority order
NAT rangesRFC 1918 (10/8, 172.16/12, 192.168/16) or 100.64.0.0/10; same prefix length as the original
ProtocolIPv4 only; IPv6 passes through untranslated
Who can configure itOnly the account that owns the transit gateway
ToolsConsole (1:1 NAT tab on the attachment), AWS CLI, SDKs, CloudFormation
MonitoringCloudWatch BytesInNAT, BytesOutNAT, PacketsInNAT, PacketsOutNAT; EventBridge events

"So nothing gets installed in my VPC?" Jake asked. "No box, no appliance, no extra subnet," Ethan said. "The rewrite happens inside the transit gateway, on the attachment."

Why overlapping CIDRs broke Transit Gateway until now

A transit gateway route table can hold only one route for a given prefix. When you attach a second VPC whose CIDR overlaps one that is already attached, the transit gateway does not propagate the newcomer's CIDR. The attachment shows available, everything looks healthy, and the second VPC simply has no route. Our route-not-propagating guide walks through that symptom in detail.

Until this week there were four ways out, and none of them was pleasant:

  • Renumber one network. The clean answer, and often a project measured in months, because addresses live in firewall rules, DNS, application settings and partner allowlists.
  • Private NAT gateways with a secondary CIDR. You give each VPC an extra, non-overlapping range, put private NAT gateways in it, and route only those ranges through the transit gateway. It works, but only outbound: the far side cannot start a connection to you, and you pay for NAT gateways in every Availability Zone (AZ) of every VPC involved.
  • PrivateLink. Publish one service behind a Network Load Balancer and consume it as an endpoint. Excellent for one service, clumsy for "these two networks need to talk".
  • NAT appliances. Firewalls or routers you run yourself, with the patching, scaling and failover that implies.

1:1 NAT replaces most of those cases with rules on the attachments you already pay for. Renumbering is still the right long-term answer for networks you control, and we come back to that in the comparison.

The trap: translate both overlapping networks, not just one

The obvious plan is to leave your own VPC alone and translate the newcomer. It fails, and the reason has nothing to do with the transit gateway.

Picture Jake's two VPCs, both on 10.0.0.0/16. Ethan puts a rule only on the booking VPC (VPC-B), mapping it to 100.65.0.0/16. An inventory server in VPC-A at 10.0.0.5 connects to a booking server at its NAT address, 100.65.0.1. The packet reaches VPC-B as src=10.0.0.5, dst=10.0.0.1. The booking server replies to 10.0.0.5, and that is where it breaks: 10.0.0.5 is inside VPC-B's own CIDR, and a VPC always delivers traffic for its own range locally, through the local route. The reply stays inside VPC-B, looking for a host that is not there. It never reaches the transit gateway.

The fix is to give each overlapping network its own NAT range:

AttachmentReal CIDR (MatchSourceCidr)NAT CIDROthers reach it at
VPC-A (inventory)10.0.0.0/16100.64.0.0/16100.64.x.y
VPC-B (booking)10.0.0.0/16100.65.0.0/16100.65.x.y

Now follow the same connection. VPC-A's 10.0.0.5 sends to 100.65.0.1. The transit gateway applies VPC-A's rule to the source (10.0.0.5 becomes 100.64.0.5), finds the route for 100.65.0.0/16, and applies VPC-B's rule to the destination (100.65.0.1 becomes 10.0.0.1). The booking server sees src=100.64.0.5 and replies to it. That address is outside its own CIDR, so the reply follows the VPC route to the transit gateway, which reverses both rewrites. Each server uses its real address, and each network reaches the other at the other's NAT range.

The same logic applies to anything that overlaps: a partner's office on 192.168.1.0/24 and your VPC with the same block, or an on-premises network and a VPC. Translating one side is fine when the ranges do not overlap and you only want to hide addresses. When they overlap, translate both. "It feels like double the work," Jake said. "It's two rules," Ethan said. "One per side, and then the reply has somewhere to go."

The second trap: saving a rule replaces the VPC's real route

On a VPC attachment, when a rule's MatchSourceCidr exactly matches one of the VPC's CIDRs, the transit gateway does more than translate. It removes the VPC's propagated route for 10.0.0.0/16 and installs a route for the NAT CIDR instead, with the route type system. Here is a transit gateway route table before and after, with a third VPC (VPC-C, 172.16.0.0/16) that has no rules:

PrefixAttachmentRoute type
Before NAT (VPC-B overlaps VPC-A, so VPC-B is not propagated at all)
10.0.0.0/16VPC-Apropagated
172.16.0.0/16VPC-Cpropagated
After NAT on VPC-A and VPC-B
100.64.0.0/16VPC-Asystem
100.65.0.0/16VPC-Bsystem
172.16.0.0/16VPC-Cpropagated

Look at what disappeared: 10.0.0.0/16. If VPC-C, your office VPN or a Direct Connect site was reaching VPC-A at 10.0.x.y yesterday, it loses that path the moment the rule saves. From then on, everyone reaches VPC-A at 100.64.x.y, so every peer's route tables, security group rules, firewall allowlists and DNS records have to move to the NAT range. That is why the order of work matters, and why the migration plan below puts those updates first.

This bites hardest on the network you already run. Jake's inventory VPC is reached by both shops' tills over VPN. Translate it, and the tills need the new addresses on the same evening. Ethan's answer was to translate the inventory VPC with a NAT range that the tills learn over BGP before the switch, and to do it at 9 p.m. on a Sunday.

How to set up 1:1 NAT for two overlapping VPCs, step by step

This walkthrough connects two VPCs that both use 10.0.0.0/16 through one transit gateway. You need the account that owns the transit gateway; if it is shared through AWS Resource Access Manager, the accounts that own the VPCs cannot see or change NAT settings.

  1. Reserve a NAT block. Pick a range used nowhere in your AWS estate, your offices or your partners, and carve one slice per overlapping network. A slice of 100.64.0.0/10 is a sensible default: it is private, rarely used inside companies, and big enough for 64 networks of /16. Here: VPC-A gets 100.64.0.0/16, VPC-B gets 100.65.0.0/16.
  2. Attach both VPCs to the transit gateway as usual, and associate them with a route table they both propagate to. VPC-B's CIDR will not appear yet, which is expected.
  3. Check that appliance mode is off on both VPC attachments. NAT cannot be enabled on an attachment with appliance mode on.
  4. Enable NAT on VPC-A's attachment:
    aws ec2 modify-transit-gateway-attachment-nat-configuration \
        --transit-gateway-attachment-id tgw-attach-0aaaaaaaaaaaaaaaa \
        --nat-gateway-support enable \
        --nat-gateway-rules '[{"RuleNumber":100,"MatchSourceCidr":"10.0.0.0/16","NatCidr":"100.64.0.0/16"}]'
  5. Enable NAT on VPC-B's attachment with its own range:
    aws ec2 modify-transit-gateway-attachment-nat-configuration \
        --transit-gateway-attachment-id tgw-attach-0bbbbbbbbbbbbbbbb \
        --nat-gateway-support enable \
        --nat-gateway-rules '[{"RuleNumber":100,"MatchSourceCidr":"10.0.0.0/16","NatCidr":"100.65.0.0/16"}]'
  6. Wait for both to settle. Changes apply in the background while the attachment stays available. Run the check below until support reads enabled and every rule reads activated:
    aws ec2 get-transit-gateway-attachment-nat-configuration \
        --transit-gateway-attachment-id tgw-attach-0aaaaaaaaaaaaaaaa \
        --query '{Support:NatGatewaySupport,Rules:NatGatewayRules[].[RuleNumber,State]}'
  7. Update the VPC route tables. In VPC-A's subnet route tables, send 100.65.0.0/16 to the transit gateway. In VPC-B's, send 100.64.0.0/16 to the transit gateway. Each VPC routes to the other side's NAT range; nobody routes to 10.0.0.0/16 through the transit gateway any more.
  8. Update security groups and network ACLs. VPC-B now sees VPC-A's traffic arriving from 100.64.0.0/16, so its inbound rules must allow that range, and the same in reverse. Rules that allow 10.0.0.0/16 do not match translated traffic.
  9. Test with the NAT address. From a VPC-A instance, connect to a VPC-B instance at its NAT address: VPC-B's 10.0.0.1 is 100.65.0.1 from VPC-A's side.

For scripts, this small loop waits until a change has finished. It reads the support state and the state of every rule, and stops when nothing is still moving:

#!/bin/sh
# wait-nat.sh tgw-attach-ID [max-seconds]: poll until 1:1 NAT changes have settled.
ATT=$1; MAX=${2:-600}; T=0
while :; do
  OUT=$(aws ec2 get-transit-gateway-attachment-nat-configuration \
        --transit-gateway-attachment-id "$ATT" \
        --query '[NatGatewaySupport, join(`,`, NatGatewayRules[].State || `[]`)]' --output text) || exit 1
  SUP=$(echo "$OUT" | cut -f1); STATES=$(echo "$OUT" | cut -f2)
  echo "$(date -u +%H:%M:%S) support=$SUP rules=${STATES:-none}"
  case "$SUP" in enabling|disabling) ;; *)
    echo "$STATES" | grep -qE 'pending|deleting' || { echo "settled"; exit 0; } ;;
  esac
  T=$((T+15)); [ "$T" -ge "$MAX" ] && { echo "still changing after ${MAX}s"; exit 2; }
  sleep 15
done

We ran its query against sample responses for the four states that matter: a change in progress returns ['enabling', 'pending,activated'], a finished one ['enabled', 'activated,activated'], and an attachment whose rules were cleared returns ['disabled', ''] whether the response has an empty rule list or none at all, so the loop ends cleanly in every case.

"How long does the switch take?" Jake asked. "A short while, and there can be a moment in the middle where new addresses drop packets," Ethan said. "So we do it when the tills are closed."

Setting it up in the console

  1. Open the VPC console and choose Transit gateway attachments.
  2. Select the attachment, open its 1:1 NAT tab and choose Manage 1:1 NAT (also under Actions).
  3. Tick Enable NAT Gateway support.
  4. Under NAT rules, choose Add rule and enter a rule number (1 to 32766), the Match source CIDR and the NAT CIDR.
  5. Choose Save changes, then come back to the 1:1 NAT tab until it shows Enabled and every rule shows Activated.

Two console details catch people. First, the form is the whole rule set: when you save, the rules on screen replace every rule on the attachment, so a rule you delete from the form is deleted for real. Second, you cannot save while support shows Enabling or Disabling; wait for it to settle, then edit again.

CloudFormation: one resource per attachment

In CloudFormation, NAT is its own resource, AWS::EC2::TransitGatewayAttachmentNatConfiguration, not a property of the attachment resource. Each resource holds the full configuration for one attachment, and the stack must run in the account that owns the transit gateway:

Resources:
  InventoryVpcNat:
    Type: AWS::EC2::TransitGatewayAttachmentNatConfiguration
    Properties:
      TransitGatewayAttachmentId: !Ref InventoryVpcAttachment
      NatGatewaySupport: enable
      NatGatewayRules:
        - RuleNumber: 100
          MatchSourceCidr: 10.0.0.0/16
          NatCidr: 100.64.0.0/16
  BookingVpcNat:
    Type: AWS::EC2::TransitGatewayAttachmentNatConfiguration
    Properties:
      TransitGatewayAttachmentId: !Ref BookingVpcAttachment
      NatGatewaySupport: enable
      NatGatewayRules:
        - RuleNumber: 100
          MatchSourceCidr: 10.0.0.0/16
          NatCidr: 100.65.0.0/16

Keeping both sides in one template is a good habit, because the pair only works together. The NatGatewayRules list in the template is the complete set, exactly as with the CLI, so editing it replaces every rule on the attachment; review the change set before you run it. If the CloudFormation basics are new, our CloudFormation stack guide explains stacks and change sets.

NAT rule limits: what a valid rule looks like

The API checks each rule, but only within one attachment. Here is the full list of conditions:

ConditionDetailChecked by AWS?
Equal prefix lengthsA /24 maps to a /24, a /16 to a /16Yes
NAT range typeInside 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 or 100.64.0.0/10Yes
Match rangeCannot overlap multicast (224.0.0.0/4) or link-local (169.254.0.0/16)Yes
No overlap on one attachmentNo match or NAT CIDR may overlap another rule's match or NAT CIDR on the same attachmentYes
Count and numbersUp to 50 rules; unique numbers from 1 to 32766; numbers are labels, not prioritiesYes
No overlap across your networkA NAT range must not overlap any other attachment's NAT range, any untranslated attachment's CIDR, or any VPC or office that must reach itNo, that part is on you

The last row is the dangerous one. Say VPC-A is 10.0.0.0/16 and you pick 10.0.5.0/24 as a NAT range for someone else. VPC-A can never reach it: those addresses sit inside VPC-A's own range, so VPC-A delivers the traffic locally. The API accepts the rule happily, because the conflict is between two different networks. The same goes for an office that routes the range internally, or two attachments that were each given the same slice by two different engineers.

Because rules on one attachment can never overlap, every address matches at most one rule. That is why rule numbers carry no priority: there is never a contest to settle. Use them as labels with gaps (100, 200, 300), so a later rule can slot in between.

Lab: a NAT plan checker that catches what the API misses

We wrote a small checker that reads a plan as JSON (attachments, their real CIDRs, their rules and any other networks that must reach the NAT ranges) and applies every condition above, plus the cross-network checks the API leaves to you. It makes no AWS calls; it runs before you touch anything. The full script is in the code block below. Here is its output for three plans.

The correct plan from this guide (both VPCs translated, VPC-C untouched):

INFO  vpc-a: saving replaces the propagated route for 10.0.0.0/16 with 100.64.0.0/16; anything that reaches vpc-a by its real address loses the path
INFO  vpc-b: saving replaces the propagated route for 10.0.0.0/16 with 100.65.0.0/16; anything that reaches vpc-b by its real address loses the path

The one-sided plan from the trap:

INFO  vpc-b: saving replaces the propagated route for 10.0.0.0/16 with 100.65.0.0/16; anything that reaches vpc-b by its real address loses the path
ERROR vpc-a 10.0.0.0/16 overlaps vpc-b 10.0.0.0/16 but only one side is translated; vpc-a sends its replies to an address inside its own CIDR and the VPC keeps them local. Translate both.

A plan with the common mistakes in one place: a /24 rule inside a /16 VPC, a mismatched prefix, a public NAT range, an inspection VPC with appliance mode, a static VPN, a Client VPN and two ranges that collide across attachments (trimmed to the key lines):

WARN  vpc-b rule 100: 10.0.1.0/24 is not an exact VPC CIDR, so no route is installed for 10.0.5.0/24; add a static TGW route for 10.0.5.0/24 to this attachment or replies are dropped
ERROR vpc-b rule 200: 10.0.2.0/24 is /24 but 100.66.0.0/16 is /16; 1:1 NAT needs equal prefix lengths
ERROR vpc-b rule 300: NAT CIDR 8.8.8.0/24 is not RFC 1918 or 100.64.0.0/10
ERROR inspection: appliance mode is on; NAT cannot be enabled until you turn it off
WARN  branch-vpn rule 10: vpn-static never gets a system route; add a static route for 100.64.10.0/24 or have the peer advertise it
ERROR laptops: client-vpn attachments do not support 1:1 NAT
ERROR vpc-a: NAT CIDR 100.64.0.0/16 overlaps 100.64.10.0/24 used by branch-vpn (AWS only checks inside one attachment)
ERROR vpc-b: NAT CIDR 10.0.5.0/24 overlaps 10.0.0.0/16 used by vpc-a (AWS only checks inside one attachment)

The last two lines are the ones worth the script. The API would accept every one of those rules, and the collisions would only show up as traffic going to the wrong place. Save the script as tgw_nat_plan_check.py and run python3 tgw_nat_plan_check.py plan.json:

#!/usr/bin/env python3
"""Check a Transit Gateway 1:1 NAT plan before you save it (no AWS calls)."""
import ipaddress as ip, json, sys

ALLOWED = [ip.ip_network(n) for n in ('10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', '100.64.0.0/10')]
BANNED_SRC = [ip.ip_network('224.0.0.0/4'), ip.ip_network('169.254.0.0/16')]
NO_NAT_TYPES = {'client-vpn', 'network-function', 'cloudwan-peering', 'cloudwan-route-table'}
SYSTEM_ROUTE_NEVER = {'vpn-static', 'peering', 'connect', 'vpn-concentrator'}

def check(plan):
    out, atts, nat_cidrs = [], plan['attachments'], {}
    for a in atts:
        name, kind, rules = a['name'], a['type'], a.get('rules', [])
        real = [ip.ip_network(c) for c in a.get('cidrs', [])]
        if rules and kind in NO_NAT_TYPES:
            out.append(f'ERROR {name}: {kind} attachments do not support 1:1 NAT')
        if rules and a.get('appliance_mode'):
            out.append(f'ERROR {name}: appliance mode is on; NAT cannot be enabled until you turn it off')
        if len(rules) > 50:
            out.append(f'ERROR {name}: {len(rules)} rules; the limit is 50 per attachment')
        seen_nums, mine = set(), []
        for r in rules:
            n, src, dst = r['RuleNumber'], ip.ip_network(r['MatchSourceCidr']), ip.ip_network(r['NatCidr'])
            if not 1 <= n <= 32766: out.append(f'ERROR {name} rule {n}: rule number must be 1-32766')
            if n in seen_nums: out.append(f'ERROR {name} rule {n}: duplicate rule number')
            seen_nums.add(n)
            if src.prefixlen != dst.prefixlen:
                out.append(f'ERROR {name} rule {n}: {src} is /{src.prefixlen} but {dst} is /{dst.prefixlen}; 1:1 NAT needs equal prefix lengths')
            if not any(dst.subnet_of(x) for x in ALLOWED):
                out.append(f'ERROR {name} rule {n}: NAT CIDR {dst} is not RFC 1918 or 100.64.0.0/10')
            if any(src.overlaps(b) for b in BANNED_SRC):
                out.append(f'ERROR {name} rule {n}: match CIDR {src} overlaps multicast or link-local')
            for prev_n, c in mine:
                for x in (src, dst):
                    if x.overlaps(c): out.append(f'ERROR {name} rule {n}: {x} overlaps another CIDR on this attachment (rule {prev_n})')
            mine += [(n, src), (n, dst)]
            if kind == 'vpc' and src not in real:
                out.append(f'WARN  {name} rule {n}: {src} is not an exact VPC CIDR, so no route is installed for {dst}; add a static TGW route for {dst} to this attachment or replies are dropped')
            if kind in SYSTEM_ROUTE_NEVER:
                out.append(f'WARN  {name} rule {n}: {kind} never gets a system route; add a static route for {dst} or have the peer advertise it')
            nat_cidrs.setdefault(name, []).append(dst)
        if kind == 'vpc':
            for r in rules:
                if ip.ip_network(r['MatchSourceCidr']) in real:
                    out.append(f'INFO  {name}: saving replaces the propagated route for {r["MatchSourceCidr"]} with {r["NatCidr"]}; anything that reaches {name} by its real address loses the path')
    for a in atts:   # cross-attachment checks the API leaves to you
        for d in nat_cidrs.get(a['name'], []):
            for b in atts:
                if b is a: continue
                for c in [ip.ip_network(x) for x in b.get('cidrs', [])] + nat_cidrs.get(b['name'], []):
                    if d.overlaps(c): out.append(f'ERROR {a["name"]}: NAT CIDR {d} overlaps {c} used by {b["name"]} (AWS only checks inside one attachment)')
            for other in plan.get('other_networks', []):
                if d.overlaps(ip.ip_network(other)): out.append(f'ERROR {a["name"]}: NAT CIDR {d} overlaps {other}, a network that must reach it')
    for i, a in enumerate(atts):   # identical CIDRs where only one side is translated
        for b in atts[i+1:]:
            for ca in a.get('cidrs', []):
                for cb in b.get('cidrs', []):
                    if ip.ip_network(ca).overlaps(ip.ip_network(cb)):
                        ta = any(ip.ip_network(r['MatchSourceCidr']).overlaps(ip.ip_network(ca)) for r in a.get('rules', []))
                        tb = any(ip.ip_network(r['MatchSourceCidr']).overlaps(ip.ip_network(cb)) for r in b.get('rules', []))
                        if ta != tb:
                            plain = b['name'] if ta else a['name']
                            out.append(f'ERROR {a["name"]} {ca} overlaps {b["name"]} {cb} but only one side is translated; {plain} sends its replies to an address inside its own CIDR and the VPC keeps them local. Translate both.')
                        elif not ta:
                            out.append(f'ERROR {a["name"]} {ca} overlaps {b["name"]} {cb} and neither is translated; the later attachment is not propagated')
    return out or ['OK    no problems found']

if __name__ == '__main__':
    for line in check(json.load(open(sys.argv[1]))): print(line)

A plan file looks like this (types: vpc, vpn-bgp, vpn-static, dxgw, peering, connect, vpn-concentrator, client-vpn):

{"attachments": [
  {"name": "vpc-a", "type": "vpc", "cidrs": ["10.0.0.0/16"],
   "rules": [{"RuleNumber": 100, "MatchSourceCidr": "10.0.0.0/16", "NatCidr": "100.64.0.0/16"}]},
  {"name": "vpc-b", "type": "vpc", "cidrs": ["10.0.0.0/16"],
   "rules": [{"RuleNumber": 100, "MatchSourceCidr": "10.0.0.0/16", "NatCidr": "100.65.0.0/16"}]},
  {"name": "vpc-c", "type": "vpc", "cidrs": ["172.16.0.0/16"]}],
 "other_networks": ["192.168.0.0/16"]}

How routing works with 1:1 NAT, attachment by attachment

After translation, other networks reach an attachment through its NAT range, so a route to that range has to exist in every transit gateway route table that sends traffic there. Whether the transit gateway adds that route for you depends on the attachment type, through a setting called NatSystemRouteSupport:

Attachment typeNatSystemRouteSupportHow the NAT range becomes reachable
VPCAlways enableThe VPC CIDR's propagated route is replaced by the NAT route, but only when the rule exactly matches a VPC CIDR
Direct Connect gateway, Site-to-Site VPN with BGPenable (default) or disableThe transit gateway installs the route, or you advertise the NAT range yourself
Site-to-Site VPN with static routing, static peeringAlways disableAdd a static route for the NAT range to the attachment
Connect, VPN ConcentratorAlways disableYour BGP peers advertise the NAT range

Three routing details decide whether your design works:

  • A partial rule on a VPC gets no route. If the VPC is 10.0.0.0/16 and the rule matches only 10.0.1.0/24, the API accepts it and translation happens, but nothing replaces or adds a route. Until you add a static transit gateway route for the NAT range pointing at the VPC attachment, traffic to the NAT addresses, including replies to translated traffic, goes nowhere. This is the quietest failure in the feature.
  • A static route beats a NAT route. For the same prefix, a system route ranks below a static route and above prefix list references and propagated routes. Longest-prefix match still comes first. So an old static route for 100.64.0.0/16 that someone created last year still wins, and enabling NAT never overrides it.
  • NAT routes appear only in route tables the attachment propagates to. If another route table needs to reach the NAT range, add a static route there.

In route searches and exports (SearchTransitGatewayRoutes, ExportTransitGatewayRoutes), routes the transit gateway created for NAT show the type system. A NAT range that you advertise from your own BGP peers shows as propagated; the type tells you who created the route, not whether NAT is involved.

"Why would a /24 rule be useful at all?" Jake asked. "When only one subnet clashes," Ethan said. "You translate just that slice and leave the rest of the VPC on its real addresses. You just have to add the route by hand."

Partners over VPN and offices over Direct Connect

The partner case is where 1:1 NAT earns its keep. A supplier's office on 192.168.1.0/24 connects over Site-to-Site VPN, and you already use 192.168.1.0/24 in a VPC. With BGP on the VPN, you have two ways to make the NAT range reachable.

System routes on (the default). Put a rule on the VPN attachment, say 192.168.1.0/24 to 100.66.1.0/24 (and, because the ranges clash, a rule on your own VPC too, as in the trap), and the transit gateway installs a system route for 100.66.1.0/24. That route stays active only while the partner keeps advertising a route that overlaps the original range: the same 192.168.1.0/24, something more specific, or a summary. If the partner stops advertising it, perhaps because someone "cleans up" the overlapping announcement, the NAT route turns into a blackhole and traffic to the partner is dropped. The partner's original routes stay in the table next to the NAT route either way.

System routes off. Set --nat-system-route-support disable and have the partner's router advertise 100.66.1.0/24 instead. The transit gateway learns it as a propagated route. In this mode the partner can stop advertising the original range, which is cleaner when that range collides with one of yours, and you can use BGP attributes as you would on any route (AS-path prepending for active and standby, identical attributes for ECMP).

aws ec2 modify-transit-gateway-attachment-nat-configuration \
    --transit-gateway-attachment-id tgw-attach-0ccccccccccccccc0 \
    --nat-gateway-support enable \
    --nat-system-route-support disable \
    --nat-gateway-rules '[{"RuleNumber":100,"MatchSourceCidr":"192.168.1.0/24","NatCidr":"100.66.1.0/24"}]'

Pick one mode and keep the partner's behavior consistent with it. The combination to watch for is "system routes on" plus "partner stopped advertising the original": it looks tidy and drops everything. A static VPN never gets a system route, so add a static route for the NAT range that targets the VPN attachment. Direct Connect gateway attachments follow the same two modes as BGP VPN; if you are still choosing between them, our Direct Connect guide covers the costs.

For a Connect attachment (an SD-WAN appliance), you can put NAT on the Connect attachment or on its transport attachment, never both. Enabling it on the second returns an InvalidParameterValue error. To move it, disable it on the first and wait for disabled before enabling on the other.

DNS: the step nobody puts on the plan

Every server in Jake's booking VPC has a DNS name that resolves to its real 10.0.x.y address. From the inventory VPC, that real address is useless: it sits inside the inventory VPC's own range and stays local. Clients in VPC-A must connect to VPC-B's NAT addresses, so they need names that resolve to 100.65.x.y.

1:1 NAT rewrites packet headers, not DNS answers. A few ways to handle it:

  • A private hosted zone for the "other side's view". Create a Route 53 private hosted zone, associate it only with VPC-A, and add records that point VPC-B's service names to their NAT addresses (booking.internal to 100.65.0.20). Because host bits are preserved, each NAT address is simply the real one with the network part swapped, which makes the records easy to generate.
  • Load balancers in front of services. If VPC-B exposes services through internal load balancers, their addresses change when nodes are replaced, so generating records from real addresses goes stale. For a handful of services, PrivateLink can be the simpler choice.
  • Careful with shared zones. If both VPCs are associated with the same private hosted zone, clients in VPC-A get VPC-B's real addresses, which is the wrong answer for them.

Our Route 53 vs Route 53 Resolver guide explains private hosted zones and forwarding rules, and the VPC peering DNS guide covers why a name can resolve to an address you cannot reach.

"So the network works, but nobody can find anything until the names change," Jake said. "Exactly," Ethan said. "The routes take an evening. The names take a spreadsheet."

Security groups, network ACLs and logs see the NAT address

After translation, the receiving side sees the sender's NAT address as the source. That changes three things:

  • Security groups: VPC-B's inbound rules must allow 100.64.0.0/16 (VPC-A's NAT range), not 10.0.0.0/16. Write these as CIDR rules for the NAT range.
  • Network ACLs: the same, in both directions, because network ACLs are stateless. Our security group vs NACL guide shows the return-port rules.
  • Logs and audits: VPC Flow Logs in VPC-B record the NAT source address. Keep a mapping (the rule table is enough, because host bits carry over) so whoever reads the logs can turn 100.64.0.5 back into VPC-A's 10.0.0.5.

There is also a security upside: partners and other business units only ever see your NAT range. Your internal addressing never appears in their firewall logs.

What 1:1 NAT does not do

  • No IPv6. IPv6 traffic passes through untranslated.
  • No port translation. It is 1:1, not many-to-one. Each original address needs its own NAT address, so a /16 needs a whole /16 of NAT space. If you need many hosts to share a few addresses, that is a NAT gateway's job.
  • No appliance mode on the same attachment. If your inspection VPC attachment has appliance mode on, you cannot enable NAT on that attachment. Put the NAT rules on the spoke attachments instead; the firewall then sees NAT addresses, so write its rules for those.
  • Not on Client VPN, Network Function attachments, or Cloud WAN peering and route table attachments.
  • Not on both a Connect attachment and its transport.
  • Owner only. In a transit gateway shared through Resource Access Manager, only the owner account can view or modify NAT. The VPC owner's account cannot even read it.
  • Describe permissions are not enough. ec2:Describe* does not include ec2:GetTransitGatewayAttachmentNatConfiguration, so a read-only role built on Describe actions cannot see NAT settings.
  • Brief traffic loss during changes. Translation and routes are updated separately and can take effect at slightly different moments; for a short time, traffic to new NAT addresses can drop.

IAM permissions for 1:1 NAT

Two IAM actions cover the Transit Gateway side. A role that manages NAT needs both; an auditing role needs only the second:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ManageTgwNat",
      "Effect": "Allow",
      "Action": [
        "ec2:ModifyTransitGatewayAttachmentNatConfiguration",
        "ec2:GetTransitGatewayAttachmentNatConfiguration"
      ],
      "Resource": "*"
    }
  ]
}

Run both from the account that owns the transit gateway. If your existing network-admin role only lists ec2:Describe* and route-table actions, add these explicitly; wildcard ec2:Get* also covers the read action.

Because one save can cut off every peer of a VPC, it is worth limiting who can change NAT at all. In AWS Organizations, a service control policy can deny the modify action to everyone except your network role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "OnlyNetworkTeamChangesNat",
      "Effect": "Deny",
      "Action": "ec2:ModifyTransitGatewayAttachmentNatConfiguration",
      "Resource": "*",
      "Condition": {
        "ArnNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/NetworkAdmin" }
      }
    }
  ]
}

Attach it to the organizational unit that holds the network account, try a change with a non-admin role first to see the deny, and leave the read action open to auditors so they can confirm what is configured. "So even I can't break it by accident?" Jake asked. "Not unless you borrow my role," Ethan said.

1:1 NAT on Cloud WAN: same idea, different API

Cloud WAN uses the same mechanism through the Network Manager API: UpdateAttachmentNatConfiguration to change it and ListAttachmentNatConfigurations to read it, run by the core network owner. The concepts match the Transit Gateway version, but four details differ, and they break scripts copied from one to the other:

DetailTransit Gateway (EC2 API)Cloud WAN (Network Manager API)
Turn it onenable / disable (lower case); state reads enabledENABLE / DISABLE (upper case); state reads ENABLED
System routes settingString: enable / disableBoolean: true / false
Remove every rule--reset-nat-gateway-rules (cannot be combined with a rule list)Send an empty rule list
Pending attachmentsNot applicableSettings on an attachment awaiting acceptance are stored and applied after acceptance (state PENDING_ATTACHMENT_ACCEPTANCE)

In both, the rule list you send replaces the existing rules, so always send the complete set. On Cloud WAN, NAT system routes have the route type SYSTEM, and translation happens whether or not system routes are on.

On Cloud WAN, 1:1 NAT is a paid feature billed at Regional NAT gateway rates, and your normal Cloud WAN attachment and data processing charges do not change. More on cost below.

Lab: what the SDK catches, and what it lets through

Before writing automation, we checked how boto3 1.43.118, a release that already includes these APIs, validates the calls, using botocore's Stubber so no request leaves the machine. The results matter if you build rule lists in code:

CallResult in the SDK
EC2: enable plus one ruleAccepted
EC2: RuleNumber as the string "100"Rejected locally: Invalid type for parameter NatGatewayRules[0].RuleNumber ... valid types: <class 'int'>
EC2: a rule with no NatCidrAccepted locally; the SDK does not mark rule fields as required, so the mistake only surfaces as a service error
Cloud WAN: a rule with no NatCidrRejected locally: Missing required parameter in NatGatewayRules[0]: "NatCidr"
Cloud WAN: NatSystemRouteSupport="enable" (the EC2 habit)Rejected locally: Invalid type for parameter NatSystemRouteSupport, value: enable ... valid types: <class 'bool'>

If that automation runs in AWS Lambda, bundle a current boto3 with it. The Lambda Python 3.13 base image built on October 13, 2026 ships boto3 1.42.97, which has no get_transit_gateway_attachment_nat_configuration or modify_transit_gateway_attachment_nat_configuration at all; calling them there raises an AttributeError, because the APIs arrived after that SDK release.

The practical lesson: on the Transit Gateway side, validate your own rule objects (all three fields present, integers for rule numbers) before you call the API, or run the plan checker above. And when you reuse a rule-building function for Cloud WAN, convert the enable flags and the system-route setting rather than passing them through.

Monitoring translated traffic in CloudWatch

Four new metrics in the AWS/TransitGateway namespace count only translated traffic. The existing BytesIn, BytesOut, PacketsIn and PacketsOut still count everything:

  • BytesInNAT and PacketsInNAT: traffic received from an attachment whose source was translated by that attachment's rules.
  • BytesOutNAT and PacketsOutNAT: traffic sent to an attachment whose destination was translated by that attachment's rules.

Each NAT metric counts against the attachment whose rule applied. Traffic from an untranslated VPC to a translated VPC's NAT address shows up in the destination attachment's PacketsOutNAT, not in the source's PacketsInNAT. With both sides translated, as in this guide, one packet counts in the source attachment's PacketsInNAT and the destination attachment's PacketsOutNAT. The only meaningful statistic is Sum, at 60-second resolution, with the TransitGatewayAttachment dimension for per-attachment graphs.

Two alarms are worth setting on day one. Alarm on PacketDropCountBlackhole above zero for the transit gateway, which catches the VPN case where a NAT route turned into a blackhole. And watch PacketsOutNAT on each translated VPC attachment: if it falls to zero while PacketsIn on the peer stays busy, traffic is arriving at the wrong address. If CloudWatch is new, our plain-English CloudWatch guide covers alarms. 1:1 NAT also publishes events to EventBridge, which you can route to a chat channel so the team sees NAT activity as it happens.

Transit Gateway 1:1 NAT pricing

The Transit Gateway charges you already pay do not change. In US East (N. Virginia), on October 24, 2026, Transit Gateway costs $0.05 per attachment-hour for VPC, VPN, Direct Connect, Connect and peering attachments, and $0.02 per GB of data processed.

1:1 NAT is a separate, paid feature. It is billed at Regional NAT gateway rates, which adds an hourly charge and a data processing charge on top of the transit gateway charges. A Regional NAT gateway in US East (Ohio) costs $0.045 per hour per Availability Zone and $0.045 per GB processed. On October 24, AWS had not yet published a separate 1:1 NAT price line for transit gateway attachments, so the exact hourly unit (per attachment, or per Availability Zone the attachment uses) was not public yet. Check the Amazon VPC section of your bill a day after you enable it.

The data processing part is the one that grows. Every translated gigabyte pays the Transit Gateway's $0.02 plus the NAT rate of $0.045, so about $0.065 per GB instead of $0.02. For Jake's two shops, moving about 200 GB a month between the apps:

ItemMonthly (730 hours)
2 VPC attachments at $0.05 per hour$73.00
Transit Gateway data processing, 200 GB at $0.02$4.00
NAT data processing, 200 GB at $0.045$9.00
NAT hourly charge: each $0.045-per-hour unit$32.85 per unit (two attachments with NAT, possibly per AZ)

So the NAT feature adds roughly $9 per 200 GB plus at least $65.70 a month in hourly charges for the two translated attachments if the hourly charge is counted per attachment, and more if it is counted per Availability Zone. Compare that with the private-NAT-gateway workaround, which needs a private NAT gateway in each AZ of each VPC: four zonal private NAT gateways at $0.045 per hour is $131.40 a month before any data. For a busy link, data processing dominates, so measure your traffic first with BytesIn on the attachments. Our NAT gateway bill guide explains how NAT data charges build up.

"Seventy-odd dollars a month to avoid renumbering," Jake said. "That's cheaper than one evening of my time." "For now," Ethan said. "We still renumber the booking VPC next year. This buys us the year."

1:1 NAT vs renumbering vs private NAT gateway vs PrivateLink

OptionBoth sides can start connections?EffortBest for
Transit Gateway 1:1 NATYesRules on attachments; DNS, routes and security groups move to NAT rangesMergers, partners over VPN, many services across two overlapping networks
RenumberingYesHigh: every address in every configNetworks you own for the long term
Private NAT gateway + secondary CIDRNo, outbound onlyMedium: extra CIDR, subnets, gateways per AZOne-way access, or regions or partitions without 1:1 NAT
PrivateLinkConsumer to provider onlyLow per serviceExposing a few specific services
NAT applianceYesHigh: you run, patch and scale itPort translation or features 1:1 NAT lacks

PrivateLink pricing is in our VPC endpoint pricing guide. A sensible pattern for an acquisition: 1:1 NAT on day one so the businesses can work together, then renumber the smaller network on a calm timetable, then remove the rules.

A migration plan for a live network

When one of the overlapping networks is already in use, the route swap is the risk. This order keeps the outage to the change window itself:

  1. Inventory. List every CIDR in your VPCs (primary and secondary), offices, partners and other clouds. Run the plan checker against it.
  2. Prepare the peers. Add routes for the NAT ranges in every VPC route table that will use them, add security group and network ACL rules for the NAT ranges, and create the DNS records that point to NAT addresses. None of this changes traffic yet.
  3. Tell the partners. Anyone with an allowlist for your real addresses needs the NAT range, and anyone advertising routes over BGP needs to know which mode you chose.
  4. Enable NAT in a maintenance window, on both overlapping attachments, then wait until both show enabled and every rule activated.
  5. Switch the clients to the NAT addresses (or flip the DNS records), and only then remove old rules for real addresses.
  6. Watch PacketsOutNAT, PacketDropCountBlackhole and PacketDropCountNoRoute for the first day.

To back out, disable NAT on the attachment. The rules are kept in the deactivated state and come back as activated if you enable it again, so a rollback does not lose your work:

aws ec2 modify-transit-gateway-attachment-nat-configuration \
    --transit-gateway-attachment-id tgw-attach-0aaaaaaaaaaaaaaaa \
    --nat-gateway-support disable

To clear the rules completely, add --reset-nat-gateway-rules to the same command. NAT cannot stay enabled with zero rules, which is why the disable goes in the same call.

A five-minute test after you enable it

  1. get-transit-gateway-attachment-nat-configuration on each attachment: support enabled, every rule activated, nothing deleting.
  2. Search the transit gateway route table for each NAT range: one system route per translated VPC, and no static route for the same prefix pointing somewhere else.
  3. Check the VPC route tables: each side routes the other side's NAT range to the transit gateway.
  4. From one side, connect to the other side's NAT address on a port its security group allows (nc -vz 100.65.0.20 443).
  5. On the receiving server, confirm the client appears with its NAT address (for example in the web server's access log).
  6. Check CloudWatch for non-zero PacketsInNAT and PacketsOutNAT on the attachments.

A ping is a weak test here: ICMP can be blocked by security groups that allow TCP, and our ping troubleshooting guide explains why. Test the real port.

Troubleshooting Transit Gateway 1:1 NAT

Requests reach the far side, but replies never come back

Either only one of two overlapping networks is translated (the replies stay local inside the untranslated VPC), or the rule on a VPC is narrower than the VPC CIDR and no route exists for its NAT range. Translate both sides, or add a static transit gateway route for the NAT range that targets the VPC attachment.

Everything that used to reach the VPC by its real address stopped working

That is the route swap: a rule that exactly matches a VPC CIDR replaces the VPC's propagated route with the NAT route. Move those peers to the NAT range, or, if they must keep the real address, translate only the subnets that clash.

The NAT route shows as blackhole

On a BGP VPN or Direct Connect gateway with system routes on, the NAT route is active only while the far side advertises a route that overlaps the original range. Ask the partner to advertise it again, or switch to system routes off and have them advertise the NAT range.

AccessDenied when reading the NAT configuration

The role needs ec2:GetTransitGatewayAttachmentNatConfiguration, which ec2:Describe* does not include, and it must be in the account that owns the transit gateway.

The console will not save, or the CLI refuses a change

Support is still enabling or disabling. Wait for it to settle (the wait script above does this), then retry.

InvalidParameterValue when enabling NAT on a Connect or transport attachment

NAT is already enabled on the partner attachment of that pair. Disable it there, wait for disabled, then enable it where you want it.

Cannot enable NAT on the inspection VPC attachment

Appliance mode is on. Leave the inspection attachment alone and place NAT rules on the spoke attachments instead.

A rule I did not touch disappeared

The rule list replaces the whole set. Someone sent a list without it, from the console form or the CLI. Keep rules in code, in one place, and always send the complete set.

Translated traffic is blocked by the security group

The receiving side sees the NAT address. Allow the sender's NAT range, not its real CIDR.

Overlapping CIDRs and 1:1 NAT: frequently asked questions

Does Transit Gateway support overlapping CIDRs?

Yes, since October 23, 2026, through 1:1 NAT on attachments. Without NAT rules, a newly attached VPC whose CIDR overlaps an existing one is still not propagated.

How do I connect two VPCs with the same CIDR in AWS?

Attach both to a transit gateway and give each attachment a 1:1 NAT rule to its own unique NAT range, for example 100.64.0.0/16 and 100.65.0.0/16. Then route each side's NAT range in the other VPC and update security groups and DNS.

Can I translate only one of the two overlapping VPCs?

No. The untranslated VPC sends replies to an address inside its own CIDR, and its local route keeps them inside the VPC. Translate both.

What is 1:1 NAT in AWS Transit Gateway?

A static, stateless mapping from one CIDR to another of the same size on an attachment. Source addresses are translated outbound, destination addresses are translated back inbound, host bits and ports stay the same.

Which NAT CIDR ranges can I use?

Private RFC 1918 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) or the shared address space 100.64.0.0/10, with the same prefix length as the original CIDR.

How many NAT rules can an attachment have?

Up to 50, with rule numbers from 1 to 32766. Rule numbers are labels, not priorities, because rules on one attachment can never overlap.

Does 1:1 NAT work with Site-to-Site VPN and Direct Connect?

Yes. With BGP, the transit gateway can install the NAT route for you (active while the original range is advertised), or you can advertise the NAT range yourself. Static VPNs need a static route for the NAT range.

Does Transit Gateway 1:1 NAT support IPv6?

No. It translates IPv4 only, and IPv6 traffic passes through untranslated.

How much does Transit Gateway 1:1 NAT cost?

It is billed at Regional NAT gateway rates, an hourly charge plus a per-GB charge ($0.045 per GB for a Regional NAT gateway in US East), on top of the normal $0.05 per attachment-hour and $0.02 per GB Transit Gateway charges.

Is 1:1 NAT available on AWS Cloud WAN?

Yes, through the Network Manager API (UpdateAttachmentNatConfiguration and ListAttachmentNatConfigurations). The enable values are upper case there, and the system-route setting is a true or false value.

Can the account that owns a shared VPC attachment see the NAT rules?

No. Only the account that owns the transit gateway can view or modify NAT configuration, even when the transit gateway is shared through Resource Access Manager.

Does enabling 1:1 NAT cause downtime?

Briefly, possibly. Translation and routes update separately, so traffic to new NAT addresses can drop for a short time. On a VPC, the real-address route is also replaced, so peers using real addresses lose the path until they move to the NAT range.

When should I worry about overlapping CIDR ranges in a multi-account structure?

As soon as two accounts might ever need to talk through a shared transit gateway, VPN or peering. Plan non-overlapping ranges with a central IP plan from the start, and keep 1:1 NAT for the networks you did not number yourself, such as acquisitions and partners.

What architecture should I use for external partners over VPN with overlapping CIDRs and NAT?

Terminate the partner VPN on a transit gateway, put a 1:1 NAT rule on the VPN attachment mapping the partner's range to a dedicated NAT range, and translate your own overlapping VPC too if the ranges clash. The partner then reaches you, and you reach the partner, through NAT ranges only.

Are there any creative solutions for a VPN to multiple accounts with overlapping CIDRs?

Bring every account's VPC onto one shared transit gateway and give each overlapping VPC attachment its own slice of 100.64.0.0/10. The VPN then reaches every account through a unique NAT range, with no appliance in the middle.

How do I set up a VPC to VPC connection with overlapping IP space?

Use a transit gateway with 1:1 NAT on both VPC attachments, each to its own NAT range. VPC peering cannot connect overlapping CIDRs at all, and PrivateLink only fits when one side consumes a specific service.

Is there a workaround for a Site-to-Site VPN CIDR overlap with Direct Connect?

If the VPN and the Direct Connect site advertise overlapping ranges into one transit gateway, add a 1:1 NAT rule to one of those attachments (or both) so each arrives under a unique NAT range, and pick the system-route mode that matches what each site advertises.

What does a CIDR overlaps with an existing subnet error mean?

That is a different problem: inside one VPC, a new subnet's CIDR overlaps an existing subnet (error code InvalidSubnet.Conflict). Pick a free block within the VPC range or add a secondary CIDR. 1:1 NAT does not apply inside a VPC.

For readers preparing for an AWS certification

We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.

Exams that test this topic: Solutions Architect – Associate (SAA-C03) • Solutions Architect – Professional (SAP-C03 from November 17, 2026) • CloudOps Engineer – Associate (SOA-C03).

Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:

1. A company acquired another business. Both companies use VPCs with the CIDR 10.0.0.0/16, and the VPCs must communicate in both directions through an existing transit gateway without renumbering. What is the most operationally efficient solution?

Enable 1:1 NAT on both VPC attachments of the transit gateway, mapping each VPC to its own unique NAT CIDR, and route each NAT CIDR to the transit gateway in the other VPC's route tables.

2. A NAT rule was added on only one of two transit gateway VPC attachments whose CIDRs are identical. Requests reach the far side, but responses never return. Why?

The untranslated VPC sends replies to an address inside its own CIDR, which the VPC delivers locally. Each overlapping VPC needs its own NAT CIDR.

3. A partner connects over AWS Site-to-Site VPN from an address range that overlaps a VPC. The partner must reach an application only through addresses the company chooses. Which design meets this?

Terminate the VPN on a transit gateway and add a 1:1 NAT rule on the VPN attachment that maps the partner's range to a dedicated NAT CIDR, translating the overlapping VPC as well.

4. Which option cannot connect two VPCs whose CIDR blocks overlap?

VPC peering, which does not support overlapping CIDRs. A transit gateway with 1:1 NAT, AWS PrivateLink for specific services, or private NAT gateways can.

5. Many consumer VPCs with overlapping CIDRs must call a single application in a provider VPC. Only consumer-initiated connections are needed. Which solution is simplest?

AWS PrivateLink: an endpoint service behind a Network Load Balancer, with interface endpoints in each consumer VPC, which works regardless of overlapping CIDRs.

6. An operations engineer needs an alarm when a transit gateway drops traffic because a route has become a blackhole. Which CloudWatch metric should be used?

PacketDropCountBlackhole (or BytesDropCountBlackhole) in the AWS/TransitGateway namespace, with the TransitGateway or TransitGatewayAttachment dimension, using the Sum statistic.

Exam codes and dates change often. Our AWS certification changes page keeps track of what is current.

πŸ“š ALSO READ

More AWS networking guides for the same project:

📌 Bookmark this page. 1:1 NAT is a day-old feature, and the pricing line is the part most likely to change.

On Sunday night, Ethan saved four rules, two per side, and watched both attachments go from enabling to enabled. The booking app found the inventory app at a 100.64 address it had never seen before, and the tills were back on their new NAT addresses before the first one rang on Monday. "Nobody noticed," Jake said. "That's the whole job," Ethan said. If your setup behaves differently from what this page describes, please tell us. Guides like this stay right because readers write in.

📌 If you keep one line from this page

When two networks overlap, give each its own NAT range, because one-sided NAT sends the replies home to the wrong VPC, and move every peer to the NAT addresses before you save, because the real-address route disappears.

Both sides translated · same prefix length · RFC 1918 or 100.64.0.0/10 · 50 rules · IPv4 only · owner account only · October 24, 2026.

Revision note. Written October 24, 2026, the day after AWS added 1:1 NAT to Transit Gateway and Cloud WAN. "Measure twice, cut once," the old carpenters' saying goes, and with NAT ranges the measuring is the CIDR inventory.

Related