Route tables are deceptively simple objects. A destination prefix, a next hop, a priority. But the differences in how each cloud implements them — how they attach to network constructs, how they interact with CIDR addressing, what dependencies they carry, and where the hard ceilings are — diverge significantly across AWS, Azure, and GCP. If you’re designing multi-cloud architectures, running NVAs, or injecting service chaining, those differences will bite you.
This post covers the routing model for each cloud from first principles: structural scope, subnet attachment mechanics, quota ceilings, next-hop types, and the non-obvious constraints that don’t appear until you’re deep in a design.
The Structural Difference That Shapes Everything
Before getting into per-cloud quotas, understand the fundamental structural divergence:
- AWS: route tables are discrete VPC resources. You create them explicitly, attach them to subnets. One route table can serve many subnets; each subnet has exactly one route table.
- Azure: route tables (UDR tables) are subscription-scoped resources, independent of any VNet. A table can be associated to subnets across VNets, within the same region and subscription. Each subnet has zero or one UDR table. Azure also auto-generates per-subnet system routes that you cannot delete, only override.
- GCP: there is no “route table object” you create and attach. The routing table is a property of the VPC network itself, defined globally at the VPC level. Every VM controller in the VPC sees all applicable routes. There is no per-subnet routing boundary — routing policy is applied at the VPC scope, with selective application by tag.
This shapes everything: how CIDR addressing relates to route scope, how you segment routing domains, and where the interesting quota constraints live.
AWS VPC
Architecture
Every VPC has a main route table, auto-created at VPC creation. Custom route tables can be added. Subnets that are not explicitly associated with a custom table fall back to the main route table — an implicit association. Custom tables only have explicit associations.
Route tables can be associated with subnets or with gateways (edge associations), enabling ingress routing — useful for redirecting inbound traffic through an NVA or inspection appliance before it reaches a subnet.
CIDR Alignment
Each route entry specifies a destination CIDR. AWS uses longest prefix match with no concept of route priority (unlike GCP). This matters when you’re dealing with overlapping CIDRs in peering topologies — AWS does not support unicast reverse path forwarding in VPC peering, so asymmetric routing is a genuine concern when you have overlapping address spaces across peered VPCs.
Secondary CIDR blocks (up to 5 IPv4 and 5 IPv6, both expandable to 50 via quota) on a VPC create additional local routes automatically. You don’t need to add explicit routes for secondary CIDRs — they’re injected as local routes with local as the target.
Quotas (as of June 2025)
AWS made a notable change in June 2025: the default routes-per-route-table limit was raised from 50 to 500 for all accounts without existing overrides. This applies to existing and new VPCs automatically.
| Resource | Default | Adjustable |
|---|---|---|
| Route tables per VPC | 200 | Yes |
| Routes per route table | 500 (raised from 50, June 2025) | Yes |
| VPCs per Region | 5 | Yes (hundreds possible) |
| IPv4 CIDR blocks per VPC | 5 | Yes (up to 50) |
| IPv6 CIDR blocks per VPC | 5 | Yes (up to 50) |
| Subnets per VPC | 200 | Yes |
The 500-route default is a meaningful improvement for complex hub-spoke designs using TGW, multiple peering connections, or multi-firewall inspection paths that previously required careful route consolidation or splitting across tables.
Next-Hop Types
| Target | Notes |
|---|---|
local | Auto-injected for all VPC CIDR blocks. Not manually configurable. |
| Internet Gateway (IGW) | Outbound internet for public subnets |
| Egress-only IGW | IPv6 outbound-only (no inbound) |
| NAT Gateway | IPv4 outbound for private subnets |
| Virtual Private Gateway (VGW) | Site-to-Site VPN; also supports route propagation from BGP |
| Transit Gateway (TGW) | AWS’s primary hub-spoke construct; static routes or propagated from TGW route tables |
| VPC Peering connection | Direct peer routes; no transitivity |
| VPC Endpoint (Gateway type) | S3, DynamoDB — injected as prefix list targets |
| Network Interface (ENI) | Used for NVA insertion; next hop is an ENI ID |
| Instance ID | Legacy; ENI-based routing is preferred |
| Gateway Load Balancer endpoint | GWLBE; for transparent NVA insertion using GENEVE encap |
| Carrier Gateway | Wavelength Zones only |
| Outpost Local Gateway | AWS Outposts |
Route propagation from a VGW to a subnet route table is handled via BGP, and is toggled per-table. This dynamically injects routes learned over VPN/Direct Connect without manual static entry — but you need to watch the 500-route ceiling if you’re propagating large prefix tables from on-prem.
A critical constraint: you cannot use a TGW or VGW as the next hop for routes pointing to destinations within the VPC’s own CIDR space — the local route always wins for intra-VPC traffic. This matters for traffic inspection designs where you need east-west traffic between subnets to transit a firewall; you must route via a Gateway Load Balancer endpoint or ENI, not a TGW.
Azure VNet
Architecture
Azure’s routing model separates system routes from user-defined routes (UDRs). System routes are auto-created per subnet, are immutable (you cannot delete them, only override), and cover:
- VNet address space →
VirtualNetwork 0.0.0.0/0→Internet- RFC 1918 ranges not in the VNet →
None(drop)
You create a route table as an ARM resource. It is region and subscription scoped, but not VNet-scoped — the same table can be associated to subnets in different VNets, as long as they share region and subscription. Each subnet can have zero or one UDR table. When a UDR table is associated, its routes are merged with the system routes; UDRs win on conflict.
This detachment from VNet scope is architecturally significant: in a hub-spoke topology, you can maintain a single UDR table defining default routing to Azure Firewall, and associate it to spoke subnets across multiple VNets without duplication. Azure Virtual Network Manager (AVNM) can automate this at scale with UDR management in GA (API version 2025-01-01+, UseExisting mode now supported).
CIDR Alignment
System routes for a VNet are generated from the VNet address space — one route per address range. If your VNet has multiple non-contiguous CIDR blocks (up to 65,536 addresses, with a /8 to /29 constraint per range), Azure injects a separate VirtualNetwork route for each. Subnets must fall within the VNet address space; you cannot have subnet CIDRs that extend beyond the VNet prefix.
Peering inserts VirtualNetworkPeering system routes for the peer’s address space — these cannot be modified or deleted via UDR, though you can override them with a more specific UDR entry.
Service endpoint routes (VirtualNetworkServiceEndpoint) are injected automatically when you enable service endpoints on a subnet. These routes bypass UDR processing for the public IP ranges of the targeted service — relevant if you’re trying to force service traffic through an NVA.
Quotas
| Resource | Default | Notes |
|---|---|---|
| Route tables per subscription per region | 200 | Adjustable |
| UDR routes per table | 400 | Expandable to 1,000 via Azure Virtual Network Manager routing configuration |
| Routes with service tags per table | 25 | Counted against the 400 limit |
| BGP routes from VPN Gateway to on-prem | 4,000 (standard) / 10,000 (Premium) | Per ExpressRoute circuit, private peering |
| Routes advertised by ExpressRoute Gateway | 1,000 | Hard limit |
| Total routes into ExpressRoute (VNet + peering + on-prem) | 11,000 | Circuit-level aggregate; beyond this, session instability |
| Azure Route Server: routes per BGP peer | 4,000 | BGP session torn down if exceeded |
The 400-UDR ceiling is the critical design constraint in large hub-spoke topologies. If you have 400+ spoke prefixes that all need explicit routing through Azure Firewall, you hit the ceiling fast. The AVNM-managed expansion to 1,000 is the current answer, but it requires AVNM deployment — not always trivial in existing environments.
Next-Hop Types
| Type | Notes |
|---|---|
VirtualNetwork | Auto-injected for VNet address ranges; not manually configurable |
Internet | Default route for 0.0.0.0/0 in system routes |
VirtualAppliance | NVA insertion; requires explicit next-hop IP (NVA NIC or ILB IP). IP forwarding must be enabled on the NVA NIC. |
VirtualNetworkGateway | Routes traffic to VPN or ExpressRoute gateway |
None | Explicit drop; analogous to null route |
VirtualNetworkPeering | System-only; injected when peering is created |
VirtualNetworkServiceEndpoint | System-only; injected when service endpoints are enabled |
The VirtualAppliance next hop takes a private IP. This IP must be directly reachable without transiting an ExpressRoute or Virtual WAN gateway — a constraint that catches people in overlay designs. The IP can point to an Azure Internal Load Balancer (ILB) for HA NVA deployments; this is the recommended pattern for active-active NVA pairs.
One gotcha: VirtualNetworkPeering and VirtualNetworkServiceEndpoint are not valid manual next-hop types in UDRs. Azure injects these; you cannot specify them in custom routes. This means you cannot force peering traffic through a UDR-defined path that then hands off to a peer — you must use NVA-in-path with VirtualAppliance.
GCP VPC
Architecture
GCP’s model is the most different of the three. The VPC is a global resource with no regional boundary at the network level. Subnets are regional, but the routing table is per-network, not per-subnet. There is no route table object to create and attach — you create routes that are scoped to the VPC, with optional selective application via network tags (for static routes).
This means routing is flat across the VPC by default. All VMs in the VPC see the same routing table unless you use network tags to restrict which routes apply to which instances.
Cloud Router is not optional for dynamic routing — it is the only mechanism for BGP in GCP. There is no equivalent of AWS’s VGW BGP propagation into a subnet route table, or Azure’s VPN Gateway BGP injection. If you need dynamic routing (Interconnect, HA VPN, NCC hybrid spokes), you need a Cloud Router per region per VPC network.
CIDR Alignment
GCP subnets have a primary range and optional secondary ranges (for GKE pods, services, etc.). Subnet routes are automatically created for both primary and secondary ranges — these are the most preferred routes and cannot be overridden by static routes for overlapping prefixes. This is the key constraint: static routes cannot have destinations that match or are more specific than local subnet routes. If you want to intercept traffic between two VMs in the same subnet, the only mechanism is a policy-based route (evaluated before destination-based routes).
Auto mode VPCs create subnets from the 10.128.0.0/9 block — one per region. Custom mode is strongly preferred for anything production or multi-cloud, precisely because auto mode’s fixed range conflicts with typical on-premises addressing schemes.
Quotas
GCP route limits are structured differently and warrant careful attention in large environments:
| Resource | Limit | Adjustable |
|---|---|---|
| Static routes per VPC network | 250 | Yes |
| Dynamic route unique prefixes per region per VPC (from own region Cloud Routers) | 250 | No (system limit) |
| Dynamic route unique prefixes per region per VPC (from other regions, in global routing mode) | 250 | No |
| Max prefixes from a single BGP peer to Cloud Router | 5,000 | Configurable; session torn down if exceeded |
| Custom advertised routes per BGP session | 200 | Hard limit |
| Cloud Routers per project (global) | Quota; adjustable | Yes |
| Policy-based routes per project | Quota; adjustable | Yes |
The 250-prefix ceiling per region per VPC for Cloud Router dynamic routes is the most operationally impactful limit in GCP. It applies independently to each region:
- From own-region Cloud Routers: 250 unique destination prefixes
- From Cloud Routers in other regions (global routing mode): 250 unique destination prefixes from those other regions
When this ceiling is exceeded, GCP drops routes silently using deterministic lexicographic ordering — shorter prefix lengths first, then alphabetically. IPv6 prefixes are dropped before IPv4 if mask lengths are equal. The routes that get dropped are consistent (same prefix set on every restart), but you do lose reachability for the dropped destinations. This is a hard operational constraint for enterprises with large on-premises prefix tables connecting via Dedicated Interconnect.
Mitigation: prefix summarization on-prem, use of regional dynamic routing mode (limits propagation to the source region), or splitting connectivity across multiple VPCs with separate Cloud Routers.
Next-Hop Types
| Type | Notes |
|---|---|
default-internet-gateway | System-generated; auto-created 0.0.0.0/0 route per VPC |
| VM instance (instance name or IP) | Routes to a specific VM; VM must have IP forwarding enabled |
| Internal passthrough Network Load Balancer (ILB) | Recommended for HA NVA insertion; enables ECMP across backend VMs |
| VPN tunnel (HA or Classic) | Static or dynamic (BGP); dynamic requires Cloud Router |
| VLAN attachment (Interconnect) | Dynamic only; requires Cloud Router |
| Router appliance VM (NCC) | Used in NCC hybrid spokes; also requires Cloud Router for BGP |
null (drop) | Via policy-based routes only, not standard static routes |
Policy-based routes are evaluated before all other route types. They allow next-hop selection based on source IP, destination IP, and protocol — not just destination IP. This enables selective NVA insertion without needing to intercept all traffic in a subnet. The next hop for a policy-based route must be an Internal passthrough NLB (not a direct VM IP), enforcing HA at the routing layer.
GCP also supports ECMP via multiple routes to the same destination with equal priority — and via multiple backend instances behind an ILB next hop. This is the mechanism for active-active NVA forwarding at scale.
Cross-Cloud Comparison
| Dimension | AWS | Azure | GCP |
|---|---|---|---|
| Route table scope | Per VPC (subnet-attached) | Per subscription/region (subnet-attached) | Per VPC network (global) |
| Subnet attachment | 1:many (one table → many subnets) | 0 or 1 table per subnet | N/A (routing at VPC level, tags for selectivity) |
| Table count limit | 200 per VPC | 200 per subscription/region | No discrete table objects |
| Routes per table/network | 500 (default; was 50 pre-June 2025) | 400 (1,000 with AVNM) | 250 static per VPC; 250 dynamic per region from own-region |
| Dynamic route injection | BGP propagation from VGW (toggle per table) | BGP from VPN GW / ExpressRoute GW / Route Server | Cloud Router only; per region; hard 250-prefix regional ceiling |
| NVA insertion next hop | ENI ID or GWLBE | NVA private IP or ILB | ILB (internal passthrough NLB) |
| Longest-prefix match | Yes; no tie-break priority | Yes; UDR wins over system routes on same prefix | Yes; priority value (0–65535, lower = higher) as tie-breaker |
| Sub-subnet routing | No (subnet is the smallest granularity) | No | Yes (policy-based routes, source+dst+protocol) |
| Default route behaviour | No default route unless added; IGW target for 0.0.0.0/0 | System route 0.0.0.0/0 → Internet (auto) | System route 0.0.0.0/0 → default-internet-gateway (auto) |
| IPv6 support | Yes (separate routes) | Yes (separate routes) | Yes (separate default route ::/0 auto-created) |
Design Implications
AWS TGW designs: the 500-route default is now sufficient for most spoke-count topologies, but watch TGW route table counts separately (TGW route tables have their own limits). For east-west inspection, GWLBE is the correct insertion mechanism — not ENI-based routing — because GWLBE preserves symmetry for connection tracking.
Azure hub-spoke at scale: 400 UDRs hits fast in mature environments with many spoke prefixes. AVNM with routing configurations (UseExisting mode in GA since early 2025) is the path to 1,000, but plan for AVNM’s own management overhead. For ExpressRoute, stay conscious of the 11,000-route aggregate limit and the 1,000-route gateway advertisement ceiling — summarization on the gateway prefix side is now available via summarizedGatewayPrefixes.
GCP Interconnect/HA VPN with large on-prem: the 250-prefix-per-region dynamic route limit is the hardest ceiling in GCP networking. It is not adjustable. Plan for it by: (a) aggressive summarization on the on-prem side before advertising into GCP, (b) regional routing mode if routes only need to be visible in the attachment region, (c) VPC segmentation if you genuinely need more than 250 unique on-prem destinations visible per region. Cloud Router’s route dropping is deterministic and survives task restarts — but you will lose reachability for the dropped prefixes, and there’s no warning to end hosts.
A Note on Route Server / BGP Route Injection
All three clouds now have a BGP peering construct for injecting routes from NVAs without static UDR management:
- AWS: Route Server (added to VPC; BGP peering with NVA; injects routes into VPC routing table)
- Azure: Route Server (BGP peering with NVA; injects routes and redistributes to VPN/ER gateways; 4,000-route-per-peer limit; one RS per VNet; requires dedicated RouteServerSubnet)
- GCP: Cloud Router with router appliance VM (NCC hybrid spoke construct; VM acts as BGP peer; effectively the same model as Cloud Router for VLAN/VPN)
These constructs remove the operational overhead of static UDR management in dynamic NVA deployments, at the cost of additional control-plane complexity and their own quota constraints.
Route tables are where the cloud networking rubber meets the road. The limits above are current as of June 2026 — verify against provider documentation before committing to an architecture, particularly the GCP 250-prefix ceiling and Azure’s UDR count, which are the most commonly underestimated constraints in enterprise deployments.