Configure GCP Compute Routes

The gcp:compute/route:Route resource, part of the Pulumi GCP provider, defines custom routing rules that override default VPC routing behavior by specifying how packets with matching destination ranges should be forwarded. This guide focuses on three capabilities: IP-based next hops, internal load balancer routing, and cross-VPC routing via network peering.

Routes belong to VPC networks and reference next hop targets that must exist separately. The examples are intentionally small. Combine them with your own VPC infrastructure, load balancers, and network peering.

Route traffic to a specific IP address

VPCs need custom routes when traffic should bypass the default gateway and go directly to a network appliance or proxy server.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const defaultNetwork = new gcp.compute.Network("default", {name: "compute-network"});
const _default = new gcp.compute.Route("default", {
    name: "network-route",
    destRange: "15.0.0.0/24",
    network: defaultNetwork.name,
    nextHopIp: "10.132.1.5",
    priority: 100,
});
import pulumi
import pulumi_gcp as gcp

default_network = gcp.compute.Network("default", name="compute-network")
default = gcp.compute.Route("default",
    name="network-route",
    dest_range="15.0.0.0/24",
    network=default_network.name,
    next_hop_ip="10.132.1.5",
    priority=100)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		defaultNetwork, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name: pulumi.String("compute-network"),
		})
		if err != nil {
			return err
		}
		_, err = compute.NewRoute(ctx, "default", &compute.RouteArgs{
			Name:      pulumi.String("network-route"),
			DestRange: pulumi.String("15.0.0.0/24"),
			Network:   defaultNetwork.Name,
			NextHopIp: pulumi.String("10.132.1.5"),
			Priority:  pulumi.Int(100),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var defaultNetwork = new Gcp.Compute.Network("default", new()
    {
        Name = "compute-network",
    });

    var @default = new Gcp.Compute.Route("default", new()
    {
        Name = "network-route",
        DestRange = "15.0.0.0/24",
        Network = defaultNetwork.Name,
        NextHopIp = "10.132.1.5",
        Priority = 100,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Route;
import com.pulumi.gcp.compute.RouteArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var defaultNetwork = new Network("defaultNetwork", NetworkArgs.builder()
            .name("compute-network")
            .build());

        var default_ = new Route("default", RouteArgs.builder()
            .name("network-route")
            .destRange("15.0.0.0/24")
            .network(defaultNetwork.name())
            .nextHopIp("10.132.1.5")
            .priority(100)
            .build());

    }
}
resources:
  default:
    type: gcp:compute:Route
    properties:
      name: network-route
      destRange: 15.0.0.0/24
      network: ${defaultNetwork.name}
      nextHopIp: 10.132.1.5
      priority: 100
  defaultNetwork:
    type: gcp:compute:Network
    name: default
    properties:
      name: compute-network

When a packet’s destination matches the destRange (15.0.0.0/24), the route forwards it to the nextHopIp (10.132.1.5) instead of using the default gateway. The priority property (100) determines which route wins when multiple routes match the same destination; lower values take precedence.

Route through an internal load balancer

Applications that need to distribute traffic across multiple backends use internal load balancers as next hops, leveraging health checking and automatic failover.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const _default = new gcp.compute.Network("default", {
    name: "compute-network",
    autoCreateSubnetworks: false,
});
const defaultSubnetwork = new gcp.compute.Subnetwork("default", {
    name: "compute-subnet",
    ipCidrRange: "10.0.1.0/24",
    region: "us-central1",
    network: _default.id,
});
const hc = new gcp.compute.HealthCheck("hc", {
    name: "proxy-health-check",
    checkIntervalSec: 1,
    timeoutSec: 1,
    tcpHealthCheck: {
        port: 80,
    },
});
const backend = new gcp.compute.RegionBackendService("backend", {
    name: "compute-backend",
    region: "us-central1",
    healthChecks: hc.id,
});
const defaultForwardingRule = new gcp.compute.ForwardingRule("default", {
    name: "compute-forwarding-rule",
    region: "us-central1",
    loadBalancingScheme: "INTERNAL",
    backendService: backend.id,
    allPorts: true,
    network: _default.name,
    subnetwork: defaultSubnetwork.name,
});
const route_ilb = new gcp.compute.Route("route-ilb", {
    name: "route-ilb",
    destRange: "0.0.0.0/0",
    network: _default.name,
    nextHopIlb: defaultForwardingRule.id,
    priority: 2000,
});
import pulumi
import pulumi_gcp as gcp

default = gcp.compute.Network("default",
    name="compute-network",
    auto_create_subnetworks=False)
default_subnetwork = gcp.compute.Subnetwork("default",
    name="compute-subnet",
    ip_cidr_range="10.0.1.0/24",
    region="us-central1",
    network=default.id)
hc = gcp.compute.HealthCheck("hc",
    name="proxy-health-check",
    check_interval_sec=1,
    timeout_sec=1,
    tcp_health_check={
        "port": 80,
    })
backend = gcp.compute.RegionBackendService("backend",
    name="compute-backend",
    region="us-central1",
    health_checks=hc.id)
default_forwarding_rule = gcp.compute.ForwardingRule("default",
    name="compute-forwarding-rule",
    region="us-central1",
    load_balancing_scheme="INTERNAL",
    backend_service=backend.id,
    all_ports=True,
    network=default.name,
    subnetwork=default_subnetwork.name)
route_ilb = gcp.compute.Route("route-ilb",
    name="route-ilb",
    dest_range="0.0.0.0/0",
    network=default.name,
    next_hop_ilb=default_forwarding_rule.id,
    priority=2000)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := compute.NewNetwork(ctx, "default", &compute.NetworkArgs{
			Name:                  pulumi.String("compute-network"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		defaultSubnetwork, err := compute.NewSubnetwork(ctx, "default", &compute.SubnetworkArgs{
			Name:        pulumi.String("compute-subnet"),
			IpCidrRange: pulumi.String("10.0.1.0/24"),
			Region:      pulumi.String("us-central1"),
			Network:     _default.ID(),
		})
		if err != nil {
			return err
		}
		hc, err := compute.NewHealthCheck(ctx, "hc", &compute.HealthCheckArgs{
			Name:             pulumi.String("proxy-health-check"),
			CheckIntervalSec: pulumi.Int(1),
			TimeoutSec:       pulumi.Int(1),
			TcpHealthCheck: &compute.HealthCheckTcpHealthCheckArgs{
				Port: pulumi.Int(80),
			},
		})
		if err != nil {
			return err
		}
		backend, err := compute.NewRegionBackendService(ctx, "backend", &compute.RegionBackendServiceArgs{
			Name:         pulumi.String("compute-backend"),
			Region:       pulumi.String("us-central1"),
			HealthChecks: hc.ID(),
		})
		if err != nil {
			return err
		}
		defaultForwardingRule, err := compute.NewForwardingRule(ctx, "default", &compute.ForwardingRuleArgs{
			Name:                pulumi.String("compute-forwarding-rule"),
			Region:              pulumi.String("us-central1"),
			LoadBalancingScheme: pulumi.String("INTERNAL"),
			BackendService:      backend.ID(),
			AllPorts:            pulumi.Bool(true),
			Network:             _default.Name,
			Subnetwork:          defaultSubnetwork.Name,
		})
		if err != nil {
			return err
		}
		_, err = compute.NewRoute(ctx, "route-ilb", &compute.RouteArgs{
			Name:       pulumi.String("route-ilb"),
			DestRange:  pulumi.String("0.0.0.0/0"),
			Network:    _default.Name,
			NextHopIlb: defaultForwardingRule.ID(),
			Priority:   pulumi.Int(2000),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var @default = new Gcp.Compute.Network("default", new()
    {
        Name = "compute-network",
        AutoCreateSubnetworks = false,
    });

    var defaultSubnetwork = new Gcp.Compute.Subnetwork("default", new()
    {
        Name = "compute-subnet",
        IpCidrRange = "10.0.1.0/24",
        Region = "us-central1",
        Network = @default.Id,
    });

    var hc = new Gcp.Compute.HealthCheck("hc", new()
    {
        Name = "proxy-health-check",
        CheckIntervalSec = 1,
        TimeoutSec = 1,
        TcpHealthCheck = new Gcp.Compute.Inputs.HealthCheckTcpHealthCheckArgs
        {
            Port = 80,
        },
    });

    var backend = new Gcp.Compute.RegionBackendService("backend", new()
    {
        Name = "compute-backend",
        Region = "us-central1",
        HealthChecks = hc.Id,
    });

    var defaultForwardingRule = new Gcp.Compute.ForwardingRule("default", new()
    {
        Name = "compute-forwarding-rule",
        Region = "us-central1",
        LoadBalancingScheme = "INTERNAL",
        BackendService = backend.Id,
        AllPorts = true,
        Network = @default.Name,
        Subnetwork = defaultSubnetwork.Name,
    });

    var route_ilb = new Gcp.Compute.Route("route-ilb", new()
    {
        Name = "route-ilb",
        DestRange = "0.0.0.0/0",
        Network = @default.Name,
        NextHopIlb = defaultForwardingRule.Id,
        Priority = 2000,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Subnetwork;
import com.pulumi.gcp.compute.SubnetworkArgs;
import com.pulumi.gcp.compute.HealthCheck;
import com.pulumi.gcp.compute.HealthCheckArgs;
import com.pulumi.gcp.compute.inputs.HealthCheckTcpHealthCheckArgs;
import com.pulumi.gcp.compute.RegionBackendService;
import com.pulumi.gcp.compute.RegionBackendServiceArgs;
import com.pulumi.gcp.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
import com.pulumi.gcp.compute.Route;
import com.pulumi.gcp.compute.RouteArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var default_ = new Network("default", NetworkArgs.builder()
            .name("compute-network")
            .autoCreateSubnetworks(false)
            .build());

        var defaultSubnetwork = new Subnetwork("defaultSubnetwork", SubnetworkArgs.builder()
            .name("compute-subnet")
            .ipCidrRange("10.0.1.0/24")
            .region("us-central1")
            .network(default_.id())
            .build());

        var hc = new HealthCheck("hc", HealthCheckArgs.builder()
            .name("proxy-health-check")
            .checkIntervalSec(1)
            .timeoutSec(1)
            .tcpHealthCheck(HealthCheckTcpHealthCheckArgs.builder()
                .port(80)
                .build())
            .build());

        var backend = new RegionBackendService("backend", RegionBackendServiceArgs.builder()
            .name("compute-backend")
            .region("us-central1")
            .healthChecks(hc.id())
            .build());

        var defaultForwardingRule = new ForwardingRule("defaultForwardingRule", ForwardingRuleArgs.builder()
            .name("compute-forwarding-rule")
            .region("us-central1")
            .loadBalancingScheme("INTERNAL")
            .backendService(backend.id())
            .allPorts(true)
            .network(default_.name())
            .subnetwork(defaultSubnetwork.name())
            .build());

        var route_ilb = new Route("route-ilb", RouteArgs.builder()
            .name("route-ilb")
            .destRange("0.0.0.0/0")
            .network(default_.name())
            .nextHopIlb(defaultForwardingRule.id())
            .priority(2000)
            .build());

    }
}
resources:
  default:
    type: gcp:compute:Network
    properties:
      name: compute-network
      autoCreateSubnetworks: false
  defaultSubnetwork:
    type: gcp:compute:Subnetwork
    name: default
    properties:
      name: compute-subnet
      ipCidrRange: 10.0.1.0/24
      region: us-central1
      network: ${default.id}
  hc:
    type: gcp:compute:HealthCheck
    properties:
      name: proxy-health-check
      checkIntervalSec: 1
      timeoutSec: 1
      tcpHealthCheck:
        port: '80'
  backend:
    type: gcp:compute:RegionBackendService
    properties:
      name: compute-backend
      region: us-central1
      healthChecks: ${hc.id}
  defaultForwardingRule:
    type: gcp:compute:ForwardingRule
    name: default
    properties:
      name: compute-forwarding-rule
      region: us-central1
      loadBalancingScheme: INTERNAL
      backendService: ${backend.id}
      allPorts: true
      network: ${default.name}
      subnetwork: ${defaultSubnetwork.name}
  route-ilb:
    type: gcp:compute:Route
    properties:
      name: route-ilb
      destRange: 0.0.0.0/0
      network: ${default.name}
      nextHopIlb: ${defaultForwardingRule.id}
      priority: 2000

The nextHopIlb property references a ForwardingRule with loadBalancingScheme set to INTERNAL. Traffic matching the destRange (0.0.0.0/0, a default route) flows through the load balancer, which distributes it across healthy backends. The route’s priority (2000) ensures it doesn’t override more specific routes.

Route across peered networks using load balancer VIP

When networks are peered, routes can reference an internal load balancer’s IP address from the peered network, enabling traffic to flow between VPCs.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const producer = new gcp.compute.Network("producer", {
    name: "producer-vpc",
    autoCreateSubnetworks: false,
});
const producerSubnetwork = new gcp.compute.Subnetwork("producer", {
    name: "producer-subnet",
    ipCidrRange: "10.0.1.0/24",
    region: "us-central1",
    network: producer.id,
});
const consumer = new gcp.compute.Network("consumer", {
    name: "consumer-vpc",
    autoCreateSubnetworks: false,
});
const consumerSubnetwork = new gcp.compute.Subnetwork("consumer", {
    name: "consumer-subnet",
    ipCidrRange: "10.0.2.0/24",
    region: "us-central1",
    network: consumer.id,
});
const peering1 = new gcp.compute.NetworkPeering("peering1", {
    name: "peering-producer-to-consumer",
    network: consumer.id,
    peerNetwork: producer.id,
});
const peering2 = new gcp.compute.NetworkPeering("peering2", {
    name: "peering-consumer-to-producer",
    network: producer.id,
    peerNetwork: consumer.id,
});
const hc = new gcp.compute.HealthCheck("hc", {
    name: "proxy-health-check",
    checkIntervalSec: 1,
    timeoutSec: 1,
    tcpHealthCheck: {
        port: 80,
    },
});
const backend = new gcp.compute.RegionBackendService("backend", {
    name: "compute-backend",
    region: "us-central1",
    healthChecks: hc.id,
});
const _default = new gcp.compute.ForwardingRule("default", {
    name: "compute-forwarding-rule",
    region: "us-central1",
    loadBalancingScheme: "INTERNAL",
    backendService: backend.id,
    allPorts: true,
    network: producer.name,
    subnetwork: producerSubnetwork.name,
});
const route_ilb = new gcp.compute.Route("route-ilb", {
    name: "route-ilb",
    destRange: "0.0.0.0/0",
    network: consumer.name,
    nextHopIlb: _default.ipAddress,
    priority: 2000,
    tags: [
        "tag1",
        "tag2",
    ],
}, {
    dependsOn: [
        peering1,
        peering2,
    ],
});
import pulumi
import pulumi_gcp as gcp

producer = gcp.compute.Network("producer",
    name="producer-vpc",
    auto_create_subnetworks=False)
producer_subnetwork = gcp.compute.Subnetwork("producer",
    name="producer-subnet",
    ip_cidr_range="10.0.1.0/24",
    region="us-central1",
    network=producer.id)
consumer = gcp.compute.Network("consumer",
    name="consumer-vpc",
    auto_create_subnetworks=False)
consumer_subnetwork = gcp.compute.Subnetwork("consumer",
    name="consumer-subnet",
    ip_cidr_range="10.0.2.0/24",
    region="us-central1",
    network=consumer.id)
peering1 = gcp.compute.NetworkPeering("peering1",
    name="peering-producer-to-consumer",
    network=consumer.id,
    peer_network=producer.id)
peering2 = gcp.compute.NetworkPeering("peering2",
    name="peering-consumer-to-producer",
    network=producer.id,
    peer_network=consumer.id)
hc = gcp.compute.HealthCheck("hc",
    name="proxy-health-check",
    check_interval_sec=1,
    timeout_sec=1,
    tcp_health_check={
        "port": 80,
    })
backend = gcp.compute.RegionBackendService("backend",
    name="compute-backend",
    region="us-central1",
    health_checks=hc.id)
default = gcp.compute.ForwardingRule("default",
    name="compute-forwarding-rule",
    region="us-central1",
    load_balancing_scheme="INTERNAL",
    backend_service=backend.id,
    all_ports=True,
    network=producer.name,
    subnetwork=producer_subnetwork.name)
route_ilb = gcp.compute.Route("route-ilb",
    name="route-ilb",
    dest_range="0.0.0.0/0",
    network=consumer.name,
    next_hop_ilb=default.ip_address,
    priority=2000,
    tags=[
        "tag1",
        "tag2",
    ],
    opts = pulumi.ResourceOptions(depends_on=[
            peering1,
            peering2,
        ]))
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		producer, err := compute.NewNetwork(ctx, "producer", &compute.NetworkArgs{
			Name:                  pulumi.String("producer-vpc"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		producerSubnetwork, err := compute.NewSubnetwork(ctx, "producer", &compute.SubnetworkArgs{
			Name:        pulumi.String("producer-subnet"),
			IpCidrRange: pulumi.String("10.0.1.0/24"),
			Region:      pulumi.String("us-central1"),
			Network:     producer.ID(),
		})
		if err != nil {
			return err
		}
		consumer, err := compute.NewNetwork(ctx, "consumer", &compute.NetworkArgs{
			Name:                  pulumi.String("consumer-vpc"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		_, err = compute.NewSubnetwork(ctx, "consumer", &compute.SubnetworkArgs{
			Name:        pulumi.String("consumer-subnet"),
			IpCidrRange: pulumi.String("10.0.2.0/24"),
			Region:      pulumi.String("us-central1"),
			Network:     consumer.ID(),
		})
		if err != nil {
			return err
		}
		peering1, err := compute.NewNetworkPeering(ctx, "peering1", &compute.NetworkPeeringArgs{
			Name:        pulumi.String("peering-producer-to-consumer"),
			Network:     consumer.ID(),
			PeerNetwork: producer.ID(),
		})
		if err != nil {
			return err
		}
		peering2, err := compute.NewNetworkPeering(ctx, "peering2", &compute.NetworkPeeringArgs{
			Name:        pulumi.String("peering-consumer-to-producer"),
			Network:     producer.ID(),
			PeerNetwork: consumer.ID(),
		})
		if err != nil {
			return err
		}
		hc, err := compute.NewHealthCheck(ctx, "hc", &compute.HealthCheckArgs{
			Name:             pulumi.String("proxy-health-check"),
			CheckIntervalSec: pulumi.Int(1),
			TimeoutSec:       pulumi.Int(1),
			TcpHealthCheck: &compute.HealthCheckTcpHealthCheckArgs{
				Port: pulumi.Int(80),
			},
		})
		if err != nil {
			return err
		}
		backend, err := compute.NewRegionBackendService(ctx, "backend", &compute.RegionBackendServiceArgs{
			Name:         pulumi.String("compute-backend"),
			Region:       pulumi.String("us-central1"),
			HealthChecks: hc.ID(),
		})
		if err != nil {
			return err
		}
		_default, err := compute.NewForwardingRule(ctx, "default", &compute.ForwardingRuleArgs{
			Name:                pulumi.String("compute-forwarding-rule"),
			Region:              pulumi.String("us-central1"),
			LoadBalancingScheme: pulumi.String("INTERNAL"),
			BackendService:      backend.ID(),
			AllPorts:            pulumi.Bool(true),
			Network:             producer.Name,
			Subnetwork:          producerSubnetwork.Name,
		})
		if err != nil {
			return err
		}
		_, err = compute.NewRoute(ctx, "route-ilb", &compute.RouteArgs{
			Name:       pulumi.String("route-ilb"),
			DestRange:  pulumi.String("0.0.0.0/0"),
			Network:    consumer.Name,
			NextHopIlb: _default.IpAddress,
			Priority:   pulumi.Int(2000),
			Tags: pulumi.StringArray{
				pulumi.String("tag1"),
				pulumi.String("tag2"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			peering1,
			peering2,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var producer = new Gcp.Compute.Network("producer", new()
    {
        Name = "producer-vpc",
        AutoCreateSubnetworks = false,
    });

    var producerSubnetwork = new Gcp.Compute.Subnetwork("producer", new()
    {
        Name = "producer-subnet",
        IpCidrRange = "10.0.1.0/24",
        Region = "us-central1",
        Network = producer.Id,
    });

    var consumer = new Gcp.Compute.Network("consumer", new()
    {
        Name = "consumer-vpc",
        AutoCreateSubnetworks = false,
    });

    var consumerSubnetwork = new Gcp.Compute.Subnetwork("consumer", new()
    {
        Name = "consumer-subnet",
        IpCidrRange = "10.0.2.0/24",
        Region = "us-central1",
        Network = consumer.Id,
    });

    var peering1 = new Gcp.Compute.NetworkPeering("peering1", new()
    {
        Name = "peering-producer-to-consumer",
        Network = consumer.Id,
        PeerNetwork = producer.Id,
    });

    var peering2 = new Gcp.Compute.NetworkPeering("peering2", new()
    {
        Name = "peering-consumer-to-producer",
        Network = producer.Id,
        PeerNetwork = consumer.Id,
    });

    var hc = new Gcp.Compute.HealthCheck("hc", new()
    {
        Name = "proxy-health-check",
        CheckIntervalSec = 1,
        TimeoutSec = 1,
        TcpHealthCheck = new Gcp.Compute.Inputs.HealthCheckTcpHealthCheckArgs
        {
            Port = 80,
        },
    });

    var backend = new Gcp.Compute.RegionBackendService("backend", new()
    {
        Name = "compute-backend",
        Region = "us-central1",
        HealthChecks = hc.Id,
    });

    var @default = new Gcp.Compute.ForwardingRule("default", new()
    {
        Name = "compute-forwarding-rule",
        Region = "us-central1",
        LoadBalancingScheme = "INTERNAL",
        BackendService = backend.Id,
        AllPorts = true,
        Network = producer.Name,
        Subnetwork = producerSubnetwork.Name,
    });

    var route_ilb = new Gcp.Compute.Route("route-ilb", new()
    {
        Name = "route-ilb",
        DestRange = "0.0.0.0/0",
        Network = consumer.Name,
        NextHopIlb = @default.IpAddress,
        Priority = 2000,
        Tags = new[]
        {
            "tag1",
            "tag2",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            peering1,
            peering2,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.Network;
import com.pulumi.gcp.compute.NetworkArgs;
import com.pulumi.gcp.compute.Subnetwork;
import com.pulumi.gcp.compute.SubnetworkArgs;
import com.pulumi.gcp.compute.NetworkPeering;
import com.pulumi.gcp.compute.NetworkPeeringArgs;
import com.pulumi.gcp.compute.HealthCheck;
import com.pulumi.gcp.compute.HealthCheckArgs;
import com.pulumi.gcp.compute.inputs.HealthCheckTcpHealthCheckArgs;
import com.pulumi.gcp.compute.RegionBackendService;
import com.pulumi.gcp.compute.RegionBackendServiceArgs;
import com.pulumi.gcp.compute.ForwardingRule;
import com.pulumi.gcp.compute.ForwardingRuleArgs;
import com.pulumi.gcp.compute.Route;
import com.pulumi.gcp.compute.RouteArgs;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var producer = new Network("producer", NetworkArgs.builder()
            .name("producer-vpc")
            .autoCreateSubnetworks(false)
            .build());

        var producerSubnetwork = new Subnetwork("producerSubnetwork", SubnetworkArgs.builder()
            .name("producer-subnet")
            .ipCidrRange("10.0.1.0/24")
            .region("us-central1")
            .network(producer.id())
            .build());

        var consumer = new Network("consumer", NetworkArgs.builder()
            .name("consumer-vpc")
            .autoCreateSubnetworks(false)
            .build());

        var consumerSubnetwork = new Subnetwork("consumerSubnetwork", SubnetworkArgs.builder()
            .name("consumer-subnet")
            .ipCidrRange("10.0.2.0/24")
            .region("us-central1")
            .network(consumer.id())
            .build());

        var peering1 = new NetworkPeering("peering1", NetworkPeeringArgs.builder()
            .name("peering-producer-to-consumer")
            .network(consumer.id())
            .peerNetwork(producer.id())
            .build());

        var peering2 = new NetworkPeering("peering2", NetworkPeeringArgs.builder()
            .name("peering-consumer-to-producer")
            .network(producer.id())
            .peerNetwork(consumer.id())
            .build());

        var hc = new HealthCheck("hc", HealthCheckArgs.builder()
            .name("proxy-health-check")
            .checkIntervalSec(1)
            .timeoutSec(1)
            .tcpHealthCheck(HealthCheckTcpHealthCheckArgs.builder()
                .port(80)
                .build())
            .build());

        var backend = new RegionBackendService("backend", RegionBackendServiceArgs.builder()
            .name("compute-backend")
            .region("us-central1")
            .healthChecks(hc.id())
            .build());

        var default_ = new ForwardingRule("default", ForwardingRuleArgs.builder()
            .name("compute-forwarding-rule")
            .region("us-central1")
            .loadBalancingScheme("INTERNAL")
            .backendService(backend.id())
            .allPorts(true)
            .network(producer.name())
            .subnetwork(producerSubnetwork.name())
            .build());

        var route_ilb = new Route("route-ilb", RouteArgs.builder()
            .name("route-ilb")
            .destRange("0.0.0.0/0")
            .network(consumer.name())
            .nextHopIlb(default_.ipAddress())
            .priority(2000)
            .tags(            
                "tag1",
                "tag2")
            .build(), CustomResourceOptions.builder()
                .dependsOn(                
                    peering1,
                    peering2)
                .build());

    }
}
resources:
  producer:
    type: gcp:compute:Network
    properties:
      name: producer-vpc
      autoCreateSubnetworks: false
  producerSubnetwork:
    type: gcp:compute:Subnetwork
    name: producer
    properties:
      name: producer-subnet
      ipCidrRange: 10.0.1.0/24
      region: us-central1
      network: ${producer.id}
  consumer:
    type: gcp:compute:Network
    properties:
      name: consumer-vpc
      autoCreateSubnetworks: false
  consumerSubnetwork:
    type: gcp:compute:Subnetwork
    name: consumer
    properties:
      name: consumer-subnet
      ipCidrRange: 10.0.2.0/24
      region: us-central1
      network: ${consumer.id}
  peering1:
    type: gcp:compute:NetworkPeering
    properties:
      name: peering-producer-to-consumer
      network: ${consumer.id}
      peerNetwork: ${producer.id}
  peering2:
    type: gcp:compute:NetworkPeering
    properties:
      name: peering-consumer-to-producer
      network: ${producer.id}
      peerNetwork: ${consumer.id}
  hc:
    type: gcp:compute:HealthCheck
    properties:
      name: proxy-health-check
      checkIntervalSec: 1
      timeoutSec: 1
      tcpHealthCheck:
        port: '80'
  backend:
    type: gcp:compute:RegionBackendService
    properties:
      name: compute-backend
      region: us-central1
      healthChecks: ${hc.id}
  default:
    type: gcp:compute:ForwardingRule
    properties:
      name: compute-forwarding-rule
      region: us-central1
      loadBalancingScheme: INTERNAL
      backendService: ${backend.id}
      allPorts: true
      network: ${producer.name}
      subnetwork: ${producerSubnetwork.name}
  route-ilb:
    type: gcp:compute:Route
    properties:
      name: route-ilb
      destRange: 0.0.0.0/0
      network: ${consumer.name}
      nextHopIlb: ${default.ipAddress}
      priority: 2000
      tags:
        - tag1
        - tag2
    options:
      dependsOn:
        - ${peering1}
        - ${peering2}

The nextHopIlb property accepts either a forwarding rule ID or its IP address. Here, it uses the load balancer’s ipAddress from the producer network. The dependsOn ensures both peering connections are established before creating the route. The tags property applies this route only to VMs with matching tags.

Beyond these examples

These snippets focus on specific route-level features: next hop types (IP address, internal load balancer, VIP), network peering integration, and priority-based routing. They’re intentionally minimal rather than full network architectures.

The examples rely on pre-existing infrastructure such as VPC networks and subnets, internal load balancers (health checks, backend services, forwarding rules), and network peering connections. They focus on configuring the route rather than provisioning the surrounding infrastructure.

To keep things focused, common route patterns are omitted, including:

  • Internet gateway routing (nextHopGateway)
  • Instance-based routing (nextHopInstance)
  • VPN tunnel routing (nextHopVpnTunnel)
  • Tag-based route application for selective VM routing

These omissions are intentional: the goal is to illustrate how each route feature is wired, not provide drop-in networking modules. See the Compute Route resource reference for all available configuration options.

Let's configure GCP Compute Routes

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Configuration & Requirements
What route properties can I change after creation?
None. All route properties are immutable, including destRange, name, network, all nextHop* fields, priority, and tags. Any changes require recreating the route.
Can I specify multiple nextHop fields for a route?
No. A route must have exactly one of nextHopGateway, nextHopInstance, nextHopIp, nextHopVpnTunnel, or nextHopIlb.
Does GCP support IPv6 routes?
No. The destRange property only supports IPv4 addresses.
Next Hop Configuration
Can I use an IP address for nextHopIlb instead of a URL?
Yes. You can specify either an IP address (e.g., 10.128.0.56) or a forwarding rule URL. With the beta provider, IP addresses work for forwarding rules in the same VPC or peered VPCs when destRange is a public (non-RFC 1918) IP CIDR.
When do I need to specify nextHopInstanceZone?
Only when using nextHopInstance with a short instance name. Omit nextHopInstanceZone if you provide the instance as a full or partial URL.
What formats can I use for nextHopGateway?
You can use a full URL, partial URL, or the string default-internet-gateway. Valid formats include https://www.googleapis.com/compute/v1/projects/project/global/gateways/default-internet-gateway, projects/project/global/gateways/default-internet-gateway, or global/gateways/default-internet-gateway.
Routing Behavior
How does route priority work when multiple routes match a packet?
Routes prefer smaller or more specific destination ranges. If there’s a tie, the route with the lowest priority value wins (default is 1000, valid range 0-65535). If still tied, layer 3 and 4 packet headers determine the final route.
How do tags affect route application?
Routes apply to virtual machines by tag. The tags property specifies which instance tags this route applies to, forming part of each VM’s routing table.

Using a different cloud?

Explore networking guides for other cloud providers: