Azure VNet Peering: Connect Two Virtual Networks Without the Public Internet
Connect two Azure virtual networks over private IPs using VNet peering: a worked CLI configuration, gateway transit for hub-and-spoke, verification steps, and the non-transitive routing and CIDR rules that break most setups.
19 Sept 2026, 21:33 UTC

If you need resources in two Azure virtual networks to talk to each other over private IPs, VNet peering is the supported answer: traffic stays on the Azure backbone, never touches the public internet, and you avoid the cost and complexity of VPN gateways between VNets. The catch is that peering has strict rules — non-overlapping address spaces, no transitive routing, and a two-sided configuration — that cause most failed setups.
How peering actually works
A peering is a pair of links. Creating a peering from VNet A to VNet B only opens one direction; you must also create the reciprocal peering from B to A before traffic flows. Until both sides exist, the peering status shows Initiated instead of Connected.
Once connected, Azure programs routes directly into the effective routing tables of both VNets. VMs reach each other by private IP with no gateway in the path, so latency and bandwidth behave like traffic within a single VNet. Peering works within a region (regional peering) and across regions (global VNet peering), and across subscriptions and Microsoft Entra tenants, though cross-tenant setups require appropriate permissions on both sides.
A worked configuration with Azure CLI
The example below creates two VNets with non-overlapping CIDR blocks and peers them. Run these commands in Azure Cloud Shell or a local Azure CLI session (version 2.x) where you are logged in with an account that has the Network Contributor role, or equivalent permissions, on both VNets. Replace the placeholder names and address prefixes with your own.
# Create two VNets with non-overlapping address space
az network vnet create -g rg-peering-demo -n vnet-a \
--address-prefix 10.10.0.0/16 --subnet-name app --subnet-prefix 10.10.1.0/24
az network vnet create -g rg-peering-demo -n vnet-b \
--address-prefix 10.20.0.0/16 --subnet-name app --subnet-prefix 10.20.1.0/24
# Peering from A to B
az network vnet peering create -g rg-peering-demo \
--name a-to-b --vnet-name vnet-a \
--remote-vnet $(az network vnet show -g rg-peering-demo -n vnet-b --query id -o tsv) \
--allow-vnet-access
# Reciprocal peering from B to A
az network vnet peering create -g rg-peering-demo \
--name b-to-a --vnet-name vnet-b \
--remote-vnet $(az network vnet show -g rg-peering-demo -n vnet-a --query id -o tsv) \
--allow-vnet-access
--allow-vnet-access is the setting that actually permits traffic; without it the link exists but communication is blocked. This operation changes routing state, so treat it as a change to production if the VNets are in use — the safe rollback is az network vnet peering delete on both sides, which removes the injected routes.
Verify the connection before trusting it
Check that both peerings report Connected:
az network vnet peering list -g rg-peering-demo \
--vnet-name vnet-a -o table
A status of Connected confirms the control plane, but not data-plane reachability. For a real check, deploy a small VM in each VNet and test connectivity to the remote VM's private IP — for example, SSH to it, or use Test-NetConnection <private-ip> -Port 22 from a Windows VM. Note that ICMP ping is often blocked by the target's OS firewall or an NSG even when peering is healthy, so a TCP port test is more reliable evidence. If the connection fails while peering shows Connected, check NSG rules on both subnets and the VM guest firewall before suspecting the peering itself.
Gateway transit: sharing one VPN or ExpressRoute gateway
A common hub-and-spoke pattern lets spoke VNets reach on-premises networks through the hub's VPN or ExpressRoute gateway. On the hub's peering, enable Allow gateway transit; on each spoke's peering, enable Use remote gateways. The spoke then routes on-premises-bound traffic through the hub gateway without deploying its own. A spoke can use only one remote gateway, and the setting fails if the hub has no gateway deployed yet — enable it after the gateway exists.
Limits and common mistakes
- Overlapping CIDR blocks. Peering is rejected if address spaces overlap. Plan IP ranges before creating VNets; renumbering later is painful.
- No transitive routing. If A is peered to B and B is peered to C, A cannot reach C through B. Either peer A and C directly, or route through a network virtual appliance (such as Azure Firewall) in the hub with user-defined routes.
- Address space changes. Adding or removing address ranges on a peered VNet requires the peering to be in a synced state; in some cases you must delete and recreate the peering, which briefly drops connectivity. Schedule it.
- One-sided configuration. Forgetting the reciprocal peering is the most common cause of a stuck
Initiatedstatus. - Global peering constraints. Some gateway transit and certain service scenarios behave differently across regions; verify your specific pattern against current Microsoft documentation before designing around it, as these details have changed over time.
Peering is billed per GB of data transferred, with different rates for regional and global peering, so high-volume cross-region replication deserves a cost estimate before you commit.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.