VNet peering and gateway transit
-
Phase 0: Before you start
-
Phase 1: The foundation
-
Phase 2: Day-to-day operations
-
Phase 3: Enterprise architecture
-
Phase 4: Troubleshooting step by step
-
Phase 5: Final project and career
Azure VNet peering connects two virtual networks so their VMs talk over Microsoft’s backbone with private IPs, as if they were one network. In a hub-and-spoke design, peering is the road between the hub and every spoke, and gateway transit lets spokes use the hub’s VPN or ExpressRoute gateway.
Peering looks like a single click, but it has rules that confuse people for years: it’s two links, not one; it isn’t transitive; and it has to be resynced when address spaces change.
In the lab you peer the hub to both spokes, prove that spokes can’t reach each other through the hub, read effective routes, and fix a peering that fell out of sync and one that asked for a gateway that doesn’t exist yet.
| Time | You need | Cost | By the end you can |
|---|---|---|---|
| About 50 minutes | rg-saat-hub from Lesson 45, the CLI | About 5 US cents: three small test VMs for under an hour; peering data inside one region costs about USD 0.01 per GB each way | Create and check peerings, explain non-transitive routing, and fix sync and gateway transit problems |
What it is, in one line
VNet peering links two virtual networks so their resources reach each other by private IP over Microsoft’s network.
In Simple Words
Gates between gardens
Three houses have gardens side by side. The middle house builds a gate to the left garden and another gate to the right garden. Children from the left can visit the middle garden, and so can children from the right.
But a child from the left garden can’t walk through the middle garden into the right one; the middle house didn’t agree to that. That’s non-transitive peering.
And each gate needs a latch on both sides. If only one family opens their side, nobody gets through. That’s a peering link that only exists in one direction.
Engineer's View
How peering works
- Two links: a peering is created on each VNet (hub-to-spoke1 and spoke1-to-hub). Until both exist, the state is Initiated; with both, it’s Connected.
- Options per side: allow virtual network access, allow forwarded traffic (needed when a firewall or NVA forwards packets), allow gateway transit (on the hub) and use remote gateways (on the spoke).
- Not transitive: spoke1 to hub and hub to spoke2 does not give spoke1 to spoke2. Use routing through a hub firewall (Lessons 47 and 48), direct peering, or Azure Virtual Network Manager.
- Global peering: works across regions with the same features, at a higher data price.
- Address changes: after adding or changing an address range on a peered VNet, the peering shows LocalNotInSync or RemoteNotInSync until you sync it.
- Overlaps: peering between overlapping address spaces is refused, which is why Lesson 45’s IP plan matters.
- Effective routes: a NIC’s effective route table shows a VNetPeering route for each peered range.
How it works, step by step
What happens when vm-s1 pings vm-hub:
- vm-s1 (10.41.1.4) sends to 10.40.10.4.
- Its NIC’s effective routes include 10.40.0.0/16 with next hop VNetPeering.
- Azure carries the packet over the peering to vnet-hub, staying on Microsoft’s backbone.
- No NSG blocks it (the VirtualNetwork tag includes peered ranges), so vm-hub replies the same way.
- For 10.42.1.4 there is no route in vm-s1’s table except the default to the internet, so the ping goes nowhere useful.
That last step is non-transitivity in practice: vm-s1 simply has no route to spoke 2.
When something can’t reach something else across peerings, the effective route table is the first thing to read: no route, no traffic.
Peer the hub and spokes, then prove non-transitive routing
Create three small test VMs, one in the hub and one in each spoke (about 5 minutes):
az group create --name rg-saat-l46 --location centralindia --tags course=saatvedha-azure lesson=46 -o none
$hubSub = az network vnet subnet show --resource-group rg-saat-hub --vnet-name vnet-hub --name snet-shared --query id -o tsv
$s1Sub = az network vnet subnet show --resource-group rg-saat-hub --vnet-name vnet-spoke1 --name snet-app --query id -o tsv
$s2Sub = az network vnet subnet show --resource-group rg-saat-hub --vnet-name vnet-spoke2 --name snet-app --query id -o tsv
az vm create --resource-group rg-saat-l46 --name vm-hub --image Canonical:ubuntu-24_04-lts:server:latest --size Standard_B1s --subnet $hubSub --public-ip-address '""' --nsg '""' --admin-username azureuser --generate-ssh-keys --tags course=saatvedha-azure lesson=46 -o none
az vm create --resource-group rg-saat-l46 --name vm-s1 --image Canonical:ubuntu-24_04-lts:server:latest --size Standard_B1s --subnet $s1Sub --public-ip-address '""' --nsg '""' --admin-username azureuser --generate-ssh-keys --tags course=saatvedha-azure lesson=46 -o none
az vm create --resource-group rg-saat-l46 --name vm-s2 --image Canonical:ubuntu-24_04-lts:server:latest --size Standard_B1s --subnet $s2Sub --public-ip-address '""' --nsg '""' --admin-username azureuser --generate-ssh-keys --tags course=saatvedha-azure lesson=46 -o none
az vm list-ip-addresses --resource-group rg-saat-l46 --query "[].{vm:virtualMachine.name, ip:virtualMachine.network.privateIpAddresses[0]}" -o tableYou should see this:
Vm Ip
------ ----------
vm-hub 10.40.10.4
vm-s1 10.41.1.4
vm-s2 10.42.1.4Standard_B1s (1 vCPU) keeps three VMs inside a free account’s regional vCPU limit. Before peering, none of them can reach the others.
1Create one side of a peering
Peer the hub to spoke 1, and look at the state:
az network vnet peering create --resource-group rg-saat-hub --name hub-to-spoke1 --vnet-name vnet-hub --remote-vnet vnet-spoke1 --allow-vnet-access true --allow-forwarded-traffic true -o none
az network vnet peering list --resource-group rg-saat-hub --vnet-name vnet-hub --query "[].{name:name, state:peeringState, sync:peeringSyncLevel}" -o tableYou should see this:
Name State Sync
------------- --------- ------------
hub-to-spoke1 Initiated FullyInSyncInitiated means only one side exists. No traffic flows yet.
2Complete it, and add spoke 2
Run this:
az network vnet peering create --resource-group rg-saat-hub --name spoke1-to-hub --vnet-name vnet-spoke1 --remote-vnet vnet-hub --allow-vnet-access true --allow-forwarded-traffic true -o none
az network vnet peering create --resource-group rg-saat-hub --name hub-to-spoke2 --vnet-name vnet-hub --remote-vnet vnet-spoke2 --allow-vnet-access true --allow-forwarded-traffic true -o none
az network vnet peering create --resource-group rg-saat-hub --name spoke2-to-hub --vnet-name vnet-spoke2 --remote-vnet vnet-hub --allow-vnet-access true --allow-forwarded-traffic true -o none
az network vnet peering list --resource-group rg-saat-hub --vnet-name vnet-hub --query "[].{name:name, state:peeringState, sync:peeringSyncLevel}" -o tableYou should see this:
Name State Sync
------------- --------- -----------
hub-to-spoke1 Connected FullyInSync
hub-to-spoke2 Connected FullyInSync3Test: spoke to hub works, spoke to spoke doesn’t
Ping from vm-s1 to the hub, then to spoke 2:
az vm run-command invoke --resource-group rg-saat-l46 --name vm-s1 --command-id RunShellScript --scripts 'ping -c 2 -W 2 10.40.10.4 | tail -2' --query "value[0].message" -o tsv
az vm run-command invoke --resource-group rg-saat-l46 --name vm-s1 --command-id RunShellScript --scripts 'ping -c 2 -W 2 10.42.1.4 | tail -2' --query "value[0].message" -o tsvYou should see something like this:
[stdout]
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.102/1.215/1.328/0.113 ms
[stdout]
2 packets transmitted, 0 received, 100% packet loss, time 1013msSpoke 1 and spoke 2 are both connected to the hub, yet they can’t reach each other. That’s peering working exactly as designed.
4Read vm-s1’s effective routes
Run this:
az network nic show-effective-route-table --resource-group rg-saat-l46 --name vm-s1VMNic --query "value[?nextHopType!='None'].{prefix:addressPrefix[0], nextHop:nextHopType}" -o tableYou should see something like this:
Prefix NextHop
------------ -----------
10.41.0.0/16 VnetLocal
10.40.0.0/16 VNetPeering
0.0.0.0/0 InternetThere’s a route to the hub, none to 10.42.0.0/16, so traffic to spoke 2 falls to the default route and is lost. Lesson 47 adds a route that sends spoke-to-spoke traffic to the hub.


✅ Checkpoint: Both peerings are Connected, vm-s1 reaches the hub but not spoke 2, and the effective routes show why.
Break It, Then Fix It
Keep the test VMs running for both breaks.
1Break it: the hub grows and the peering goes stale
The network team adds a second range to the hub for new shared services:
az network vnet update --resource-group rg-saat-hub --name vnet-hub --add addressSpace.addressPrefixes 10.140.0.0/16 -o none
az network vnet peering list --resource-group rg-saat-hub --vnet-name vnet-hub --query "[].{name:name, state:peeringState, sync:peeringSyncLevel}" -o tableYou should see something like this:
Name State Sync
------------- --------- ---------------
hub-to-spoke1 Connected LocalNotInSync
hub-to-spoke2 Connected LocalNotInSyncThe peerings still say Connected, so dashboards look green, but the spokes don’t know about 10.140.0.0/16. Any VM placed there would be unreachable from the spokes.
Fix it: sync both sides of each peering, then check:
az network vnet peering sync --resource-group rg-saat-hub --vnet-name vnet-hub --name hub-to-spoke1 -o none
az network vnet peering sync --resource-group rg-saat-hub --vnet-name vnet-hub --name hub-to-spoke2 -o none
az network vnet peering sync --resource-group rg-saat-hub --vnet-name vnet-spoke1 --name spoke1-to-hub -o none
az network vnet peering sync --resource-group rg-saat-hub --vnet-name vnet-spoke2 --name spoke2-to-hub -o none
az network vnet peering list --resource-group rg-saat-hub --vnet-name vnet-hub --query "[].{name:name, state:peeringState, sync:peeringSyncLevel}" -o tableBoth should show FullyInSync. Then remove the test range so the course plan stays clean:
az network vnet update --resource-group rg-saat-hub --name vnet-hub --remove addressSpace.addressPrefixes 1 -o none
az network vnet peering sync --resource-group rg-saat-hub --vnet-name vnet-hub --name hub-to-spoke1 -o none
az network vnet peering sync --resource-group rg-saat-hub --vnet-name vnet-hub --name hub-to-spoke2 -o none2Break it: using a gateway that isn’t there
Spoke 1 should reach “on-premises” through the hub’s VPN gateway, so someone turns on gateway transit:
az network vnet peering update --resource-group rg-saat-hub --vnet-name vnet-spoke1 --name spoke1-to-hub --set useRemoteGateways=trueYou’ll get an error like this:
(RemoteVnetHasNoGateways) Peering /subscriptions/.../vnet-spoke1/virtualNetworkPeerings/spoke1-to-hub cannot have UseRemoteGateway flag set to true because remote virtual network /subscriptions/.../vnet-hub referenced by the peering does not have any gateways.Gateway transit needs a real VPN or ExpressRoute gateway in the hub, and allowGatewayTransit on the hub side. The hub gets its VPN gateway in Lesson 49, where you’ll switch this on properly.
Fix it: leave useRemoteGateways off until the gateway exists. Clean up the test VMs and keep the peerings for Lesson 47:
az group delete --name rg-saat-l46 --yes
az network vnet peering list --resource-group rg-saat-hub --vnet-name vnet-hub --query "[].{name:name, state:peeringState, sync:peeringSyncLevel}" -o tableYou should see both peerings Connected and FullyInSync.

✅ Checkpoint: You synced peerings after an address change and saw why gateway transit needs a gateway. The peerings stay; the test VMs are gone.
This happened at work
The new subnet nobody could reach
A logistics company ran out of space in its hub VNet and added a second address range for a new DNS resolver. The resolver came up fine, but VMs in 30 spokes couldn’t resolve anything through it, and only some new apps failed, which made the problem hard to spot.
Every hub peering showed Connected in the portal. The sync column, which nobody had added to their checks, said LocalNotInSync on all 30. The spokes had never learned the new range.
What they changed: address space changes on peered VNets now include a sync step in the runbook, done by script for every peering. Their monitoring queries Resource Graph daily for any peering whose sync level isn’t FullyInSync, and Azure Virtual Network Manager now manages hub connectivity for new spokes.
Common mistakes and troubleshooting
| Mistake | What you see | What to do |
|---|---|---|
| Creating only one side | State Initiated; no traffic | Create the peering on both VNets. |
| Expecting transitive routing | Spoke to spoke fails | Route through a hub firewall or peer directly. |
| Changing address space without sync | LocalNotInSync / RemoteNotInSync | Run az network vnet peering sync on both sides. |
| useRemoteGateways without a hub gateway | RemoteVnetHasNoGateways | Deploy the gateway; allow gateway transit on the hub. |
| Allow forwarded traffic off with a firewall | Firewall-forwarded traffic dropped | Turn it on for peerings behind an NVA or firewall. |
| Overlapping ranges | Peering refused | Fix the IP plan first (Lesson 45). |
Interview questions
Click a question to open it. Each one is written the way an interviewer would ask it. Under it you will see why they ask it and one way you could answer, in your own words.
Fresher level
1. What is VNet peering?
Why they ask this: The basic concept.
How you might answer: A connection between two VNets that lets resources communicate by private IP over Microsoft’s backbone, in the same region or across regions.
2. Why does a peering show Initiated?
Why they ask this: A practical check.
How you might answer: Only one side has been created. Peering needs a link on each VNet; once both exist, the state becomes Connected.
3. Is VNet peering transitive?
Why they ask this: A classic interview question.
How you might answer: No. If A peers with B and B peers with C, A can’t reach C through B unless you add routing through an appliance in B or peer A and C directly.
2–5 years experience
4. A VM in one spoke can’t reach a VM in another. What do you check?
Why they ask this: A real troubleshooting path.
How you might answer: The effective routes on the source NIC: if there’s no route to the other spoke, it’s non-transitive peering. If routes go to a hub firewall, check firewall rules, allow forwarded traffic on the peerings, NSGs, and the return route.
5. What does gateway transit do?
Why they ask this: They want the hybrid link.
How you might answer: It lets spokes use the hub’s VPN or ExpressRoute gateway to reach on-premises. The hub side allows gateway transit, the spoke side uses remote gateways, and a gateway must exist in the hub.
6. What happens after you add an address range to a peered VNet?
Why they ask this: A newer-feature check.
How you might answer: The peering keeps working for old ranges but shows not in sync. You sync each side so the peered VNets learn the new range.
Senior level
7. How would you connect 200 spokes to hubs in three regions?
Why they ask this: They want scale.
How you might answer: Azure Virtual Network Manager with hub-and-spoke connectivity configurations per region, or Virtual WAN. Network groups by tag add new spokes automatically, and global peering or the hub-to-hub links carry cross-region traffic through firewalls.
8. Peering or VPN between two VNets?
Why they ask this: They want trade-offs.
How you might answer: Peering: lower latency, higher bandwidth, no gateway cost, simple. VNet-to-VNet VPN: encrypted tunnels and transit features through gateways, but slower and paid by the hour. Peering is the default unless a specific requirement says otherwise.
9. How do you monitor peerings in production?
Why they ask this: They want operations thinking.
How you might answer: Resource Graph queries or Azure Policy for peering state and sync level, alerts when anything isn’t Connected and FullyInSync, Connection Monitor for real traffic tests, and change control on address spaces.
Quick quiz
Q1. Only hub-to-spoke1 exists. Peering state?
- Connected
- Initiated
- Disconnected
- Pending
Show answer
B — The other side is missing.
Q2. spoke1 and spoke2 are both peered to the hub. Can spoke1 reach spoke2?
- Yes
- No, not without routing
- Only with NSGs
- Only in another region
Show answer
B — Peering is not transitive.
Q3. After adding a range to the hub, peerings show LocalNotInSync. Fix?
- Delete the VNet
- az network vnet peering sync
- Restart VMs
- Add NSG rules
Show answer
B — Sync both sides of each peering.
Q4. useRemoteGateways fails with RemoteVnetHasNoGateways. Why?
- Wrong region
- No gateway in the hub
- Overlap
- Missing NSG
Show answer
B — Gateway transit needs a gateway.
Q5. Which NextHopType appears for a peered range?
- VnetLocal
- VNetPeering
- Internet
- None
Show answer
B — Effective routes show VNetPeering.
Quick recap
Your hub and spokes are now connected, and you know exactly what peering does and doesn’t do.
- A peering needs a link on each side to be Connected.
- Peering isn’t transitive; spokes need routing through the hub.
- Read effective routes first when traffic fails.
- Sync peerings after address space changes.
- Gateway transit needs a gateway in the hub.
Next lesson
Lesson 47: Route tables and user-defined routes → You send spoke-to-spoke traffic through the hub with user-defined routes and fix asymmetric routing.
Further reading: Azure VNet peering
Want to go deeper into Azure VNet peering? See Microsoft’s Virtual network peering and Update the address space for a peered virtual network. You can also see the full plan for this course on the Azure Cloud Administration course page.
