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 matching a destination range should be forwarded. This guide focuses on three capabilities: IP-based routing, internal load balancer integration, and cross-VPC routing through peering.

Routes belong to VPC networks and reference next-hop targets like IP addresses, load balancers, or instances that must exist separately. The examples are intentionally small. Combine them with your own VPC infrastructure and routing requirements.

Route traffic to a specific IP address

VPC networks need custom routes when traffic should bypass default routing rules, such as directing packets to a specific appliance or gateway IP.

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, GCP forwards it to the IP specified in nextHopIp. The priority property breaks ties when multiple routes match the same destination; lower values win. This route applies to all instances in the network unless you add tags to restrict it.

Route through an internal load balancer

Applications that need to distribute traffic across backend instances often route packets through an internal load balancer rather than directly to instances.

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 points to a forwarding rule with loadBalancingScheme set to INTERNAL. When packets match the destRange, GCP sends them to the load balancer, which distributes them across healthy backends. This pattern centralizes traffic management and enables health-based routing.

Route across peered VPCs using load balancer IP

When VPCs are peered, routes can direct traffic from one network to a load balancer in another network using the load balancer’s IP address.

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}

This configuration creates a route in the consumer VPC that forwards traffic to a load balancer in the producer VPC. The nextHopIlb property uses the forwarding rule’s ipAddress rather than its ID. The dependsOn ensures both peering connections are established before creating the route. The tags property restricts this route to instances with matching tags.

Beyond these examples

These snippets focus on specific route-level features: IP-based and load balancer routing, VPC peering integration, and route priority and tagging. They’re intentionally minimal rather than full network architectures.

The examples may reference pre-existing infrastructure such as VPC networks and subnets, health checks and backend services, and forwarding rules for load balancers. They focus on configuring the route rather than provisioning everything around it.

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

  • Gateway-based routing (nextHopGateway for internet gateway)
  • Instance-based routing (nextHopInstance)
  • VPN tunnel routing (nextHopVpnTunnel)
  • Route descriptions and metadata

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

Route Configuration & Next Hops
Which nextHop property should I use for my route?
You must specify exactly one of nextHopGateway, nextHopInstance, nextHopIp, nextHopVpnTunnel, or nextHopIlb. Choose based on your routing target: gateway for internet, instance for a specific VM, IP for a network address, VPN tunnel for VPN traffic, or ILB for internal load balancer.
How do I specify nextHopIlb for an internal load balancer?
You can use either the forwarding rule’s IP address (e.g., 10.128.0.56) or a full/partial URL (e.g., regions/region/forwardingRules/forwardingRule). Note that IP addresses only work when destRange is a public (non-RFC 1918) CIDR range.
When do I need to specify nextHopInstanceZone?
You must provide nextHopInstanceZone when using nextHopInstance with just the instance name or a partial URL. Omit it if you’re using the full instance URL.
Immutability & Lifecycle
What properties can't I change after creating a route?
All core properties are immutable: destRange, name, network, all nextHop* fields, priority, tags, and description. Changing any of these requires destroying and recreating the route.
Can I modify the destination range of an existing route?
No, destRange is immutable. You’ll need to create a new route with the desired destination range and delete the old one.
Route Selection & Priority
How does GCP choose between multiple matching routes?
GCP selects routes by: (1) most specific prefix length, (2) lowest priority value if prefix lengths match, (3) layer 3/4 packet headers if there’s still a tie. Priority ranges from 0 to 65535, with a default of 1000.
What happens to packets that don't match any route?
Packets that don’t match any route in the VM’s routing table are dropped.
Advanced Scenarios
How do I route traffic to an ILB in a peered VPC?
Use nextHopIlb with the forwarding rule’s IP address and add dependsOn for the network peering resources to ensure peering is established before the route is created.
How do I apply a route to specific VMs only?
Use the tags property with a list of instance tags. The route will only apply to VMs that have matching tags.
What naming constraints apply to routes?
Route names must be 1-63 characters long and match the regex a-z?. The first character must be a lowercase letter, followed by dashes, lowercase letters, or digits, and the last character cannot be a dash.

Using a different cloud?

Explore networking guides for other cloud providers: