The gcp:compute/regionNetworkEndpointGroup:RegionNetworkEndpointGroup resource, part of the Pulumi GCP provider, defines regional network endpoint groups that route load balancer traffic to serverless services, Private Service Connect endpoints, or external backends. This guide focuses on three capabilities: serverless backend integration, Private Service Connect configuration, and external endpoint routing.
Regional NEGs reference existing Cloud Run services, Cloud Functions, App Engine versions, VPC networks, and Service Attachments. The examples are intentionally small. Combine them with your own load balancer configuration and backend services.
Route traffic to Cloud Run services
Load balancers distribute traffic to serverless Cloud Run services without managing individual endpoints. Regional NEGs provide a stable target that automatically tracks service revisions.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const cloudrunNegService = new gcp.cloudrun.Service("cloudrun_neg", {
name: "cloudrun-neg",
location: "us-central1",
template: {
spec: {
containers: [{
image: "us-docker.pkg.dev/cloudrun/container/hello",
}],
},
},
traffics: [{
percent: 100,
latestRevision: true,
}],
});
// Cloud Run Example
const cloudrunNeg = new gcp.compute.RegionNetworkEndpointGroup("cloudrun_neg", {
name: "cloudrun-neg",
networkEndpointType: "SERVERLESS",
region: "us-central1",
cloudRun: {
service: cloudrunNegService.name,
},
});
import pulumi
import pulumi_gcp as gcp
cloudrun_neg_service = gcp.cloudrun.Service("cloudrun_neg",
name="cloudrun-neg",
location="us-central1",
template={
"spec": {
"containers": [{
"image": "us-docker.pkg.dev/cloudrun/container/hello",
}],
},
},
traffics=[{
"percent": 100,
"latest_revision": True,
}])
# Cloud Run Example
cloudrun_neg = gcp.compute.RegionNetworkEndpointGroup("cloudrun_neg",
name="cloudrun-neg",
network_endpoint_type="SERVERLESS",
region="us-central1",
cloud_run={
"service": cloudrun_neg_service.name,
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudrun"
"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 {
cloudrunNegService, err := cloudrun.NewService(ctx, "cloudrun_neg", &cloudrun.ServiceArgs{
Name: pulumi.String("cloudrun-neg"),
Location: pulumi.String("us-central1"),
Template: &cloudrun.ServiceTemplateArgs{
Spec: &cloudrun.ServiceTemplateSpecArgs{
Containers: cloudrun.ServiceTemplateSpecContainerArray{
&cloudrun.ServiceTemplateSpecContainerArgs{
Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/hello"),
},
},
},
},
Traffics: cloudrun.ServiceTrafficArray{
&cloudrun.ServiceTrafficArgs{
Percent: pulumi.Int(100),
LatestRevision: pulumi.Bool(true),
},
},
})
if err != nil {
return err
}
// Cloud Run Example
_, err = compute.NewRegionNetworkEndpointGroup(ctx, "cloudrun_neg", &compute.RegionNetworkEndpointGroupArgs{
Name: pulumi.String("cloudrun-neg"),
NetworkEndpointType: pulumi.String("SERVERLESS"),
Region: pulumi.String("us-central1"),
CloudRun: &compute.RegionNetworkEndpointGroupCloudRunArgs{
Service: cloudrunNegService.Name,
},
})
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 cloudrunNegService = new Gcp.CloudRun.Service("cloudrun_neg", new()
{
Name = "cloudrun-neg",
Location = "us-central1",
Template = new Gcp.CloudRun.Inputs.ServiceTemplateArgs
{
Spec = new Gcp.CloudRun.Inputs.ServiceTemplateSpecArgs
{
Containers = new[]
{
new Gcp.CloudRun.Inputs.ServiceTemplateSpecContainerArgs
{
Image = "us-docker.pkg.dev/cloudrun/container/hello",
},
},
},
},
Traffics = new[]
{
new Gcp.CloudRun.Inputs.ServiceTrafficArgs
{
Percent = 100,
LatestRevision = true,
},
},
});
// Cloud Run Example
var cloudrunNeg = new Gcp.Compute.RegionNetworkEndpointGroup("cloudrun_neg", new()
{
Name = "cloudrun-neg",
NetworkEndpointType = "SERVERLESS",
Region = "us-central1",
CloudRun = new Gcp.Compute.Inputs.RegionNetworkEndpointGroupCloudRunArgs
{
Service = cloudrunNegService.Name,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudrun.Service;
import com.pulumi.gcp.cloudrun.ServiceArgs;
import com.pulumi.gcp.cloudrun.inputs.ServiceTemplateArgs;
import com.pulumi.gcp.cloudrun.inputs.ServiceTemplateSpecArgs;
import com.pulumi.gcp.cloudrun.inputs.ServiceTrafficArgs;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroup;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroupArgs;
import com.pulumi.gcp.compute.inputs.RegionNetworkEndpointGroupCloudRunArgs;
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 cloudrunNegService = new Service("cloudrunNegService", ServiceArgs.builder()
.name("cloudrun-neg")
.location("us-central1")
.template(ServiceTemplateArgs.builder()
.spec(ServiceTemplateSpecArgs.builder()
.containers(ServiceTemplateSpecContainerArgs.builder()
.image("us-docker.pkg.dev/cloudrun/container/hello")
.build())
.build())
.build())
.traffics(ServiceTrafficArgs.builder()
.percent(100)
.latestRevision(true)
.build())
.build());
// Cloud Run Example
var cloudrunNeg = new RegionNetworkEndpointGroup("cloudrunNeg", RegionNetworkEndpointGroupArgs.builder()
.name("cloudrun-neg")
.networkEndpointType("SERVERLESS")
.region("us-central1")
.cloudRun(RegionNetworkEndpointGroupCloudRunArgs.builder()
.service(cloudrunNegService.name())
.build())
.build());
}
}
resources:
# Cloud Run Example
cloudrunNeg:
type: gcp:compute:RegionNetworkEndpointGroup
name: cloudrun_neg
properties:
name: cloudrun-neg
networkEndpointType: SERVERLESS
region: us-central1
cloudRun:
service: ${cloudrunNegService.name}
cloudrunNegService:
type: gcp:cloudrun:Service
name: cloudrun_neg
properties:
name: cloudrun-neg
location: us-central1
template:
spec:
containers:
- image: us-docker.pkg.dev/cloudrun/container/hello
traffics:
- percent: 100
latestRevision: true
The networkEndpointType property set to SERVERLESS indicates this NEG routes to serverless backends. The cloudRun block specifies which Cloud Run service receives traffic. The NEG automatically discovers and tracks the service’s endpoints as revisions deploy.
Route traffic to Cloud Functions
HTTP-triggered Cloud Functions serve as backend targets for load balancers, enabling custom domains, SSL termination, and traffic management.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const bucket = new gcp.storage.Bucket("bucket", {
name: "cloudfunctions-function-example-bucket",
location: "US",
});
const archive = new gcp.storage.BucketObject("archive", {
name: "index.zip",
bucket: bucket.name,
source: new pulumi.asset.FileAsset("path/to/index.zip"),
});
const functionNegFunction = new gcp.cloudfunctions.Function("function_neg", {
name: "function-neg",
description: "My function",
runtime: "nodejs20",
availableMemoryMb: 128,
sourceArchiveBucket: bucket.name,
sourceArchiveObject: archive.name,
triggerHttp: true,
timeout: 60,
entryPoint: "helloGET",
});
// Cloud Functions Example
const functionNeg = new gcp.compute.RegionNetworkEndpointGroup("function_neg", {
name: "function-neg",
networkEndpointType: "SERVERLESS",
region: "us-central1",
cloudFunction: {
"function": functionNegFunction.name,
},
});
import pulumi
import pulumi_gcp as gcp
bucket = gcp.storage.Bucket("bucket",
name="cloudfunctions-function-example-bucket",
location="US")
archive = gcp.storage.BucketObject("archive",
name="index.zip",
bucket=bucket.name,
source=pulumi.FileAsset("path/to/index.zip"))
function_neg_function = gcp.cloudfunctions.Function("function_neg",
name="function-neg",
description="My function",
runtime="nodejs20",
available_memory_mb=128,
source_archive_bucket=bucket.name,
source_archive_object=archive.name,
trigger_http=True,
timeout=60,
entry_point="helloGET")
# Cloud Functions Example
function_neg = gcp.compute.RegionNetworkEndpointGroup("function_neg",
name="function-neg",
network_endpoint_type="SERVERLESS",
region="us-central1",
cloud_function={
"function": function_neg_function.name,
})
package main
import (
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudfunctions"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/storage"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
bucket, err := storage.NewBucket(ctx, "bucket", &storage.BucketArgs{
Name: pulumi.String("cloudfunctions-function-example-bucket"),
Location: pulumi.String("US"),
})
if err != nil {
return err
}
archive, err := storage.NewBucketObject(ctx, "archive", &storage.BucketObjectArgs{
Name: pulumi.String("index.zip"),
Bucket: bucket.Name,
Source: pulumi.NewFileAsset("path/to/index.zip"),
})
if err != nil {
return err
}
functionNegFunction, err := cloudfunctions.NewFunction(ctx, "function_neg", &cloudfunctions.FunctionArgs{
Name: pulumi.String("function-neg"),
Description: pulumi.String("My function"),
Runtime: pulumi.String("nodejs20"),
AvailableMemoryMb: pulumi.Int(128),
SourceArchiveBucket: bucket.Name,
SourceArchiveObject: archive.Name,
TriggerHttp: pulumi.Bool(true),
Timeout: pulumi.Int(60),
EntryPoint: pulumi.String("helloGET"),
})
if err != nil {
return err
}
// Cloud Functions Example
_, err = compute.NewRegionNetworkEndpointGroup(ctx, "function_neg", &compute.RegionNetworkEndpointGroupArgs{
Name: pulumi.String("function-neg"),
NetworkEndpointType: pulumi.String("SERVERLESS"),
Region: pulumi.String("us-central1"),
CloudFunction: &compute.RegionNetworkEndpointGroupCloudFunctionArgs{
Function: functionNegFunction.Name,
},
})
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 bucket = new Gcp.Storage.Bucket("bucket", new()
{
Name = "cloudfunctions-function-example-bucket",
Location = "US",
});
var archive = new Gcp.Storage.BucketObject("archive", new()
{
Name = "index.zip",
Bucket = bucket.Name,
Source = new FileAsset("path/to/index.zip"),
});
var functionNegFunction = new Gcp.CloudFunctions.Function("function_neg", new()
{
Name = "function-neg",
Description = "My function",
Runtime = "nodejs20",
AvailableMemoryMb = 128,
SourceArchiveBucket = bucket.Name,
SourceArchiveObject = archive.Name,
TriggerHttp = true,
Timeout = 60,
EntryPoint = "helloGET",
});
// Cloud Functions Example
var functionNeg = new Gcp.Compute.RegionNetworkEndpointGroup("function_neg", new()
{
Name = "function-neg",
NetworkEndpointType = "SERVERLESS",
Region = "us-central1",
CloudFunction = new Gcp.Compute.Inputs.RegionNetworkEndpointGroupCloudFunctionArgs
{
Function = functionNegFunction.Name,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.storage.Bucket;
import com.pulumi.gcp.storage.BucketArgs;
import com.pulumi.gcp.storage.BucketObject;
import com.pulumi.gcp.storage.BucketObjectArgs;
import com.pulumi.gcp.cloudfunctions.Function;
import com.pulumi.gcp.cloudfunctions.FunctionArgs;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroup;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroupArgs;
import com.pulumi.gcp.compute.inputs.RegionNetworkEndpointGroupCloudFunctionArgs;
import com.pulumi.asset.FileAsset;
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 bucket = new Bucket("bucket", BucketArgs.builder()
.name("cloudfunctions-function-example-bucket")
.location("US")
.build());
var archive = new BucketObject("archive", BucketObjectArgs.builder()
.name("index.zip")
.bucket(bucket.name())
.source(new FileAsset("path/to/index.zip"))
.build());
var functionNegFunction = new Function("functionNegFunction", FunctionArgs.builder()
.name("function-neg")
.description("My function")
.runtime("nodejs20")
.availableMemoryMb(128)
.sourceArchiveBucket(bucket.name())
.sourceArchiveObject(archive.name())
.triggerHttp(true)
.timeout(60)
.entryPoint("helloGET")
.build());
// Cloud Functions Example
var functionNeg = new RegionNetworkEndpointGroup("functionNeg", RegionNetworkEndpointGroupArgs.builder()
.name("function-neg")
.networkEndpointType("SERVERLESS")
.region("us-central1")
.cloudFunction(RegionNetworkEndpointGroupCloudFunctionArgs.builder()
.function(functionNegFunction.name())
.build())
.build());
}
}
resources:
# Cloud Functions Example
functionNeg:
type: gcp:compute:RegionNetworkEndpointGroup
name: function_neg
properties:
name: function-neg
networkEndpointType: SERVERLESS
region: us-central1
cloudFunction:
function: ${functionNegFunction.name}
functionNegFunction:
type: gcp:cloudfunctions:Function
name: function_neg
properties:
name: function-neg
description: My function
runtime: nodejs20
availableMemoryMb: 128
sourceArchiveBucket: ${bucket.name}
sourceArchiveObject: ${archive.name}
triggerHttp: true
timeout: 60
entryPoint: helloGET
bucket:
type: gcp:storage:Bucket
properties:
name: cloudfunctions-function-example-bucket
location: US
archive:
type: gcp:storage:BucketObject
properties:
name: index.zip
bucket: ${bucket.name}
source:
fn::FileAsset: path/to/index.zip
The cloudFunction block identifies the function by name. Like Cloud Run NEGs, this configuration uses networkEndpointType SERVERLESS and automatically tracks the function’s endpoint. Only one serverless backend type (cloudRun, cloudFunction, or appEngine) can be specified per NEG.
Route traffic to App Engine services
App Engine applications receive traffic through load balancers by specifying both the service name and version ID.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const appengineNegBucket = new gcp.storage.Bucket("appengine_neg", {
name: "appengine-neg",
location: "US",
uniformBucketLevelAccess: true,
});
const appengineNegBucketObject = new gcp.storage.BucketObject("appengine_neg", {
name: "hello-world.zip",
bucket: appengineNegBucket.name,
source: new pulumi.asset.FileAsset("./test-fixtures/hello-world.zip"),
});
const appengineNegFlexibleAppVersion = new gcp.appengine.FlexibleAppVersion("appengine_neg", {
versionId: "v1",
service: "appengine-neg",
runtime: "nodejs",
flexibleRuntimeSettings: {
operatingSystem: "ubuntu22",
runtimeVersion: "20",
},
entrypoint: {
shell: "node ./app.js",
},
deployment: {
zip: {
sourceUrl: pulumi.interpolate`https://storage.googleapis.com/${appengineNegBucket.name}/${appengineNegBucketObject.name}`,
},
},
livenessCheck: {
path: "/",
},
readinessCheck: {
path: "/",
},
envVariables: {
port: "8080",
},
handlers: [{
urlRegex: ".*\\/my-path\\/*",
securityLevel: "SECURE_ALWAYS",
login: "LOGIN_REQUIRED",
authFailAction: "AUTH_FAIL_ACTION_REDIRECT",
staticFiles: {
path: "my-other-path",
uploadPathRegex: ".*\\/my-path\\/*",
},
}],
automaticScaling: {
coolDownPeriod: "120s",
cpuUtilization: {
targetUtilization: 0.5,
},
},
deleteServiceOnDestroy: true,
});
// App Engine Example
const appengineNeg = new gcp.compute.RegionNetworkEndpointGroup("appengine_neg", {
name: "appengine-neg",
networkEndpointType: "SERVERLESS",
region: "us-central1",
appEngine: {
service: appengineNegFlexibleAppVersion.service,
version: appengineNegFlexibleAppVersion.versionId,
},
});
import pulumi
import pulumi_gcp as gcp
appengine_neg_bucket = gcp.storage.Bucket("appengine_neg",
name="appengine-neg",
location="US",
uniform_bucket_level_access=True)
appengine_neg_bucket_object = gcp.storage.BucketObject("appengine_neg",
name="hello-world.zip",
bucket=appengine_neg_bucket.name,
source=pulumi.FileAsset("./test-fixtures/hello-world.zip"))
appengine_neg_flexible_app_version = gcp.appengine.FlexibleAppVersion("appengine_neg",
version_id="v1",
service="appengine-neg",
runtime="nodejs",
flexible_runtime_settings={
"operating_system": "ubuntu22",
"runtime_version": "20",
},
entrypoint={
"shell": "node ./app.js",
},
deployment={
"zip": {
"source_url": pulumi.Output.all(
appengineNegBucketName=appengine_neg_bucket.name,
appengineNegBucketObjectName=appengine_neg_bucket_object.name
).apply(lambda resolved_outputs: f"https://storage.googleapis.com/{resolved_outputs['appengineNegBucketName']}/{resolved_outputs['appengineNegBucketObjectName']}")
,
},
},
liveness_check={
"path": "/",
},
readiness_check={
"path": "/",
},
env_variables={
"port": "8080",
},
handlers=[{
"url_regex": ".*\\/my-path\\/*",
"security_level": "SECURE_ALWAYS",
"login": "LOGIN_REQUIRED",
"auth_fail_action": "AUTH_FAIL_ACTION_REDIRECT",
"static_files": {
"path": "my-other-path",
"upload_path_regex": ".*\\/my-path\\/*",
},
}],
automatic_scaling={
"cool_down_period": "120s",
"cpu_utilization": {
"target_utilization": 0.5,
},
},
delete_service_on_destroy=True)
# App Engine Example
appengine_neg = gcp.compute.RegionNetworkEndpointGroup("appengine_neg",
name="appengine-neg",
network_endpoint_type="SERVERLESS",
region="us-central1",
app_engine={
"service": appengine_neg_flexible_app_version.service,
"version": appengine_neg_flexible_app_version.version_id,
})
package main
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/appengine"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/compute"
"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/storage"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
appengineNegBucket, err := storage.NewBucket(ctx, "appengine_neg", &storage.BucketArgs{
Name: pulumi.String("appengine-neg"),
Location: pulumi.String("US"),
UniformBucketLevelAccess: pulumi.Bool(true),
})
if err != nil {
return err
}
appengineNegBucketObject, err := storage.NewBucketObject(ctx, "appengine_neg", &storage.BucketObjectArgs{
Name: pulumi.String("hello-world.zip"),
Bucket: appengineNegBucket.Name,
Source: pulumi.NewFileAsset("./test-fixtures/hello-world.zip"),
})
if err != nil {
return err
}
appengineNegFlexibleAppVersion, err := appengine.NewFlexibleAppVersion(ctx, "appengine_neg", &appengine.FlexibleAppVersionArgs{
VersionId: pulumi.String("v1"),
Service: pulumi.String("appengine-neg"),
Runtime: pulumi.String("nodejs"),
FlexibleRuntimeSettings: &appengine.FlexibleAppVersionFlexibleRuntimeSettingsArgs{
OperatingSystem: pulumi.String("ubuntu22"),
RuntimeVersion: pulumi.String("20"),
},
Entrypoint: &appengine.FlexibleAppVersionEntrypointArgs{
Shell: pulumi.String("node ./app.js"),
},
Deployment: &appengine.FlexibleAppVersionDeploymentArgs{
Zip: &appengine.FlexibleAppVersionDeploymentZipArgs{
SourceUrl: pulumi.All(appengineNegBucket.Name, appengineNegBucketObject.Name).ApplyT(func(_args []interface{}) (string, error) {
appengineNegBucketName := _args[0].(string)
appengineNegBucketObjectName := _args[1].(string)
return fmt.Sprintf("https://storage.googleapis.com/%v/%v", appengineNegBucketName, appengineNegBucketObjectName), nil
}).(pulumi.StringOutput),
},
},
LivenessCheck: &appengine.FlexibleAppVersionLivenessCheckArgs{
Path: pulumi.String("/"),
},
ReadinessCheck: &appengine.FlexibleAppVersionReadinessCheckArgs{
Path: pulumi.String("/"),
},
EnvVariables: pulumi.StringMap{
"port": pulumi.String("8080"),
},
Handlers: appengine.FlexibleAppVersionHandlerArray{
&appengine.FlexibleAppVersionHandlerArgs{
UrlRegex: pulumi.String(".*\\/my-path\\/*"),
SecurityLevel: pulumi.String("SECURE_ALWAYS"),
Login: pulumi.String("LOGIN_REQUIRED"),
AuthFailAction: pulumi.String("AUTH_FAIL_ACTION_REDIRECT"),
StaticFiles: &appengine.FlexibleAppVersionHandlerStaticFilesArgs{
Path: pulumi.String("my-other-path"),
UploadPathRegex: pulumi.String(".*\\/my-path\\/*"),
},
},
},
AutomaticScaling: &appengine.FlexibleAppVersionAutomaticScalingArgs{
CoolDownPeriod: pulumi.String("120s"),
CpuUtilization: &appengine.FlexibleAppVersionAutomaticScalingCpuUtilizationArgs{
TargetUtilization: pulumi.Float64(0.5),
},
},
DeleteServiceOnDestroy: pulumi.Bool(true),
})
if err != nil {
return err
}
// App Engine Example
_, err = compute.NewRegionNetworkEndpointGroup(ctx, "appengine_neg", &compute.RegionNetworkEndpointGroupArgs{
Name: pulumi.String("appengine-neg"),
NetworkEndpointType: pulumi.String("SERVERLESS"),
Region: pulumi.String("us-central1"),
AppEngine: &compute.RegionNetworkEndpointGroupAppEngineArgs{
Service: appengineNegFlexibleAppVersion.Service,
Version: appengineNegFlexibleAppVersion.VersionId,
},
})
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 appengineNegBucket = new Gcp.Storage.Bucket("appengine_neg", new()
{
Name = "appengine-neg",
Location = "US",
UniformBucketLevelAccess = true,
});
var appengineNegBucketObject = new Gcp.Storage.BucketObject("appengine_neg", new()
{
Name = "hello-world.zip",
Bucket = appengineNegBucket.Name,
Source = new FileAsset("./test-fixtures/hello-world.zip"),
});
var appengineNegFlexibleAppVersion = new Gcp.AppEngine.FlexibleAppVersion("appengine_neg", new()
{
VersionId = "v1",
Service = "appengine-neg",
Runtime = "nodejs",
FlexibleRuntimeSettings = new Gcp.AppEngine.Inputs.FlexibleAppVersionFlexibleRuntimeSettingsArgs
{
OperatingSystem = "ubuntu22",
RuntimeVersion = "20",
},
Entrypoint = new Gcp.AppEngine.Inputs.FlexibleAppVersionEntrypointArgs
{
Shell = "node ./app.js",
},
Deployment = new Gcp.AppEngine.Inputs.FlexibleAppVersionDeploymentArgs
{
Zip = new Gcp.AppEngine.Inputs.FlexibleAppVersionDeploymentZipArgs
{
SourceUrl = Output.Tuple(appengineNegBucket.Name, appengineNegBucketObject.Name).Apply(values =>
{
var appengineNegBucketName = values.Item1;
var appengineNegBucketObjectName = values.Item2;
return $"https://storage.googleapis.com/{appengineNegBucketName}/{appengineNegBucketObjectName}";
}),
},
},
LivenessCheck = new Gcp.AppEngine.Inputs.FlexibleAppVersionLivenessCheckArgs
{
Path = "/",
},
ReadinessCheck = new Gcp.AppEngine.Inputs.FlexibleAppVersionReadinessCheckArgs
{
Path = "/",
},
EnvVariables =
{
{ "port", "8080" },
},
Handlers = new[]
{
new Gcp.AppEngine.Inputs.FlexibleAppVersionHandlerArgs
{
UrlRegex = ".*\\/my-path\\/*",
SecurityLevel = "SECURE_ALWAYS",
Login = "LOGIN_REQUIRED",
AuthFailAction = "AUTH_FAIL_ACTION_REDIRECT",
StaticFiles = new Gcp.AppEngine.Inputs.FlexibleAppVersionHandlerStaticFilesArgs
{
Path = "my-other-path",
UploadPathRegex = ".*\\/my-path\\/*",
},
},
},
AutomaticScaling = new Gcp.AppEngine.Inputs.FlexibleAppVersionAutomaticScalingArgs
{
CoolDownPeriod = "120s",
CpuUtilization = new Gcp.AppEngine.Inputs.FlexibleAppVersionAutomaticScalingCpuUtilizationArgs
{
TargetUtilization = 0.5,
},
},
DeleteServiceOnDestroy = true,
});
// App Engine Example
var appengineNeg = new Gcp.Compute.RegionNetworkEndpointGroup("appengine_neg", new()
{
Name = "appengine-neg",
NetworkEndpointType = "SERVERLESS",
Region = "us-central1",
AppEngine = new Gcp.Compute.Inputs.RegionNetworkEndpointGroupAppEngineArgs
{
Service = appengineNegFlexibleAppVersion.Service,
Version = appengineNegFlexibleAppVersion.VersionId,
},
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.storage.Bucket;
import com.pulumi.gcp.storage.BucketArgs;
import com.pulumi.gcp.storage.BucketObject;
import com.pulumi.gcp.storage.BucketObjectArgs;
import com.pulumi.gcp.appengine.FlexibleAppVersion;
import com.pulumi.gcp.appengine.FlexibleAppVersionArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionFlexibleRuntimeSettingsArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionEntrypointArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionDeploymentArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionDeploymentZipArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionLivenessCheckArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionReadinessCheckArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionHandlerArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionHandlerStaticFilesArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionAutomaticScalingArgs;
import com.pulumi.gcp.appengine.inputs.FlexibleAppVersionAutomaticScalingCpuUtilizationArgs;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroup;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroupArgs;
import com.pulumi.gcp.compute.inputs.RegionNetworkEndpointGroupAppEngineArgs;
import com.pulumi.asset.FileAsset;
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 appengineNegBucket = new Bucket("appengineNegBucket", BucketArgs.builder()
.name("appengine-neg")
.location("US")
.uniformBucketLevelAccess(true)
.build());
var appengineNegBucketObject = new BucketObject("appengineNegBucketObject", BucketObjectArgs.builder()
.name("hello-world.zip")
.bucket(appengineNegBucket.name())
.source(new FileAsset("./test-fixtures/hello-world.zip"))
.build());
var appengineNegFlexibleAppVersion = new FlexibleAppVersion("appengineNegFlexibleAppVersion", FlexibleAppVersionArgs.builder()
.versionId("v1")
.service("appengine-neg")
.runtime("nodejs")
.flexibleRuntimeSettings(FlexibleAppVersionFlexibleRuntimeSettingsArgs.builder()
.operatingSystem("ubuntu22")
.runtimeVersion("20")
.build())
.entrypoint(FlexibleAppVersionEntrypointArgs.builder()
.shell("node ./app.js")
.build())
.deployment(FlexibleAppVersionDeploymentArgs.builder()
.zip(FlexibleAppVersionDeploymentZipArgs.builder()
.sourceUrl(Output.tuple(appengineNegBucket.name(), appengineNegBucketObject.name()).applyValue(values -> {
var appengineNegBucketName = values.t1;
var appengineNegBucketObjectName = values.t2;
return String.format("https://storage.googleapis.com/%s/%s", appengineNegBucketName,appengineNegBucketObjectName);
}))
.build())
.build())
.livenessCheck(FlexibleAppVersionLivenessCheckArgs.builder()
.path("/")
.build())
.readinessCheck(FlexibleAppVersionReadinessCheckArgs.builder()
.path("/")
.build())
.envVariables(Map.of("port", "8080"))
.handlers(FlexibleAppVersionHandlerArgs.builder()
.urlRegex(".*\\/my-path\\/*")
.securityLevel("SECURE_ALWAYS")
.login("LOGIN_REQUIRED")
.authFailAction("AUTH_FAIL_ACTION_REDIRECT")
.staticFiles(FlexibleAppVersionHandlerStaticFilesArgs.builder()
.path("my-other-path")
.uploadPathRegex(".*\\/my-path\\/*")
.build())
.build())
.automaticScaling(FlexibleAppVersionAutomaticScalingArgs.builder()
.coolDownPeriod("120s")
.cpuUtilization(FlexibleAppVersionAutomaticScalingCpuUtilizationArgs.builder()
.targetUtilization(0.5)
.build())
.build())
.deleteServiceOnDestroy(true)
.build());
// App Engine Example
var appengineNeg = new RegionNetworkEndpointGroup("appengineNeg", RegionNetworkEndpointGroupArgs.builder()
.name("appengine-neg")
.networkEndpointType("SERVERLESS")
.region("us-central1")
.appEngine(RegionNetworkEndpointGroupAppEngineArgs.builder()
.service(appengineNegFlexibleAppVersion.service())
.version(appengineNegFlexibleAppVersion.versionId())
.build())
.build());
}
}
resources:
# App Engine Example
appengineNeg:
type: gcp:compute:RegionNetworkEndpointGroup
name: appengine_neg
properties:
name: appengine-neg
networkEndpointType: SERVERLESS
region: us-central1
appEngine:
service: ${appengineNegFlexibleAppVersion.service}
version: ${appengineNegFlexibleAppVersion.versionId}
appengineNegFlexibleAppVersion:
type: gcp:appengine:FlexibleAppVersion
name: appengine_neg
properties:
versionId: v1
service: appengine-neg
runtime: nodejs
flexibleRuntimeSettings:
operatingSystem: ubuntu22
runtimeVersion: '20'
entrypoint:
shell: node ./app.js
deployment:
zip:
sourceUrl: https://storage.googleapis.com/${appengineNegBucket.name}/${appengineNegBucketObject.name}
livenessCheck:
path: /
readinessCheck:
path: /
envVariables:
port: '8080'
handlers:
- urlRegex: .*\/my-path\/*
securityLevel: SECURE_ALWAYS
login: LOGIN_REQUIRED
authFailAction: AUTH_FAIL_ACTION_REDIRECT
staticFiles:
path: my-other-path
uploadPathRegex: .*\/my-path\/*
automaticScaling:
coolDownPeriod: 120s
cpuUtilization:
targetUtilization: 0.5
deleteServiceOnDestroy: true
appengineNegBucket:
type: gcp:storage:Bucket
name: appengine_neg
properties:
name: appengine-neg
location: US
uniformBucketLevelAccess: true
appengineNegBucketObject:
type: gcp:storage:BucketObject
name: appengine_neg
properties:
name: hello-world.zip
bucket: ${appengineNegBucket.name}
source:
fn::FileAsset: ./test-fixtures/hello-world.zip
The appEngine block requires both service and version properties to target a specific App Engine deployment. This allows you to route traffic to particular versions while maintaining other versions for testing or gradual rollouts.
Connect to Google APIs via Private Service Connect
Private Service Connect enables private connectivity to Google APIs without traversing the public internet, improving security and reducing latency.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const pscNeg = new gcp.compute.RegionNetworkEndpointGroup("psc_neg", {
name: "psc-neg",
region: "asia-northeast3",
networkEndpointType: "PRIVATE_SERVICE_CONNECT",
pscTargetService: "asia-northeast3-cloudkms.googleapis.com",
});
import pulumi
import pulumi_gcp as gcp
psc_neg = gcp.compute.RegionNetworkEndpointGroup("psc_neg",
name="psc-neg",
region="asia-northeast3",
network_endpoint_type="PRIVATE_SERVICE_CONNECT",
psc_target_service="asia-northeast3-cloudkms.googleapis.com")
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 {
_, err := compute.NewRegionNetworkEndpointGroup(ctx, "psc_neg", &compute.RegionNetworkEndpointGroupArgs{
Name: pulumi.String("psc-neg"),
Region: pulumi.String("asia-northeast3"),
NetworkEndpointType: pulumi.String("PRIVATE_SERVICE_CONNECT"),
PscTargetService: pulumi.String("asia-northeast3-cloudkms.googleapis.com"),
})
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 pscNeg = new Gcp.Compute.RegionNetworkEndpointGroup("psc_neg", new()
{
Name = "psc-neg",
Region = "asia-northeast3",
NetworkEndpointType = "PRIVATE_SERVICE_CONNECT",
PscTargetService = "asia-northeast3-cloudkms.googleapis.com",
});
});
package generated_program;
import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroup;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroupArgs;
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 pscNeg = new RegionNetworkEndpointGroup("pscNeg", RegionNetworkEndpointGroupArgs.builder()
.name("psc-neg")
.region("asia-northeast3")
.networkEndpointType("PRIVATE_SERVICE_CONNECT")
.pscTargetService("asia-northeast3-cloudkms.googleapis.com")
.build());
}
}
resources:
pscNeg:
type: gcp:compute:RegionNetworkEndpointGroup
name: psc_neg
properties:
name: psc-neg
region: asia-northeast3
networkEndpointType: PRIVATE_SERVICE_CONNECT
pscTargetService: asia-northeast3-cloudkms.googleapis.com
The networkEndpointType switches to PRIVATE_SERVICE_CONNECT for PSC endpoints. The pscTargetService property specifies the Google API endpoint (in this case, Cloud KMS in the asia-northeast3 region). Traffic flows through private VPC connectivity rather than the public internet.
Route to external endpoints by IP and port
Load balancers can distribute traffic to external services identified by IP address and port, enabling hybrid architectures that span GCP and external infrastructure.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.compute.Network("default", {name: "network"});
const regionNetworkEndpointGroupInternetIpPort = new gcp.compute.RegionNetworkEndpointGroup("region_network_endpoint_group_internet_ip_port", {
name: "ip-port-neg",
region: "us-central1",
network: _default.id,
networkEndpointType: "INTERNET_IP_PORT",
});
import pulumi
import pulumi_gcp as gcp
default = gcp.compute.Network("default", name="network")
region_network_endpoint_group_internet_ip_port = gcp.compute.RegionNetworkEndpointGroup("region_network_endpoint_group_internet_ip_port",
name="ip-port-neg",
region="us-central1",
network=default.id,
network_endpoint_type="INTERNET_IP_PORT")
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("network"),
})
if err != nil {
return err
}
_, err = compute.NewRegionNetworkEndpointGroup(ctx, "region_network_endpoint_group_internet_ip_port", &compute.RegionNetworkEndpointGroupArgs{
Name: pulumi.String("ip-port-neg"),
Region: pulumi.String("us-central1"),
Network: _default.ID(),
NetworkEndpointType: pulumi.String("INTERNET_IP_PORT"),
})
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 = "network",
});
var regionNetworkEndpointGroupInternetIpPort = new Gcp.Compute.RegionNetworkEndpointGroup("region_network_endpoint_group_internet_ip_port", new()
{
Name = "ip-port-neg",
Region = "us-central1",
Network = @default.Id,
NetworkEndpointType = "INTERNET_IP_PORT",
});
});
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.RegionNetworkEndpointGroup;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroupArgs;
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("network")
.build());
var regionNetworkEndpointGroupInternetIpPort = new RegionNetworkEndpointGroup("regionNetworkEndpointGroupInternetIpPort", RegionNetworkEndpointGroupArgs.builder()
.name("ip-port-neg")
.region("us-central1")
.network(default_.id())
.networkEndpointType("INTERNET_IP_PORT")
.build());
}
}
resources:
regionNetworkEndpointGroupInternetIpPort:
type: gcp:compute:RegionNetworkEndpointGroup
name: region_network_endpoint_group_internet_ip_port
properties:
name: ip-port-neg
region: us-central1
network: ${default.id}
networkEndpointType: INTERNET_IP_PORT
default:
type: gcp:compute:Network
properties:
name: network
The INTERNET_IP_PORT endpoint type routes traffic to external IP:port combinations. The network property specifies which VPC network the NEG belongs to. You add individual endpoints separately using the RegionNetworkEndpoint resource.
Route to external endpoints by FQDN and port
When external services use DNS names rather than static IPs, FQDN-based NEGs automatically resolve hostnames and track IP changes.
import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";
const _default = new gcp.compute.Network("default", {name: "network"});
const regionNetworkEndpointGroupInternetFqdnPort = new gcp.compute.RegionNetworkEndpointGroup("region_network_endpoint_group_internet_fqdn_port", {
name: "ip-port-neg",
region: "us-central1",
network: _default.id,
networkEndpointType: "INTERNET_FQDN_PORT",
});
import pulumi
import pulumi_gcp as gcp
default = gcp.compute.Network("default", name="network")
region_network_endpoint_group_internet_fqdn_port = gcp.compute.RegionNetworkEndpointGroup("region_network_endpoint_group_internet_fqdn_port",
name="ip-port-neg",
region="us-central1",
network=default.id,
network_endpoint_type="INTERNET_FQDN_PORT")
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("network"),
})
if err != nil {
return err
}
_, err = compute.NewRegionNetworkEndpointGroup(ctx, "region_network_endpoint_group_internet_fqdn_port", &compute.RegionNetworkEndpointGroupArgs{
Name: pulumi.String("ip-port-neg"),
Region: pulumi.String("us-central1"),
Network: _default.ID(),
NetworkEndpointType: pulumi.String("INTERNET_FQDN_PORT"),
})
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 = "network",
});
var regionNetworkEndpointGroupInternetFqdnPort = new Gcp.Compute.RegionNetworkEndpointGroup("region_network_endpoint_group_internet_fqdn_port", new()
{
Name = "ip-port-neg",
Region = "us-central1",
Network = @default.Id,
NetworkEndpointType = "INTERNET_FQDN_PORT",
});
});
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.RegionNetworkEndpointGroup;
import com.pulumi.gcp.compute.RegionNetworkEndpointGroupArgs;
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("network")
.build());
var regionNetworkEndpointGroupInternetFqdnPort = new RegionNetworkEndpointGroup("regionNetworkEndpointGroupInternetFqdnPort", RegionNetworkEndpointGroupArgs.builder()
.name("ip-port-neg")
.region("us-central1")
.network(default_.id())
.networkEndpointType("INTERNET_FQDN_PORT")
.build());
}
}
resources:
regionNetworkEndpointGroupInternetFqdnPort:
type: gcp:compute:RegionNetworkEndpointGroup
name: region_network_endpoint_group_internet_fqdn_port
properties:
name: ip-port-neg
region: us-central1
network: ${default.id}
networkEndpointType: INTERNET_FQDN_PORT
default:
type: gcp:compute:Network
properties:
name: network
The INTERNET_FQDN_PORT endpoint type works like INTERNET_IP_PORT but accepts fully qualified domain names instead of IP addresses. The NEG resolves DNS automatically and adapts to IP changes without manual updates.
Beyond these examples
These snippets focus on specific regional NEG features: serverless backend integration, Private Service Connect for Google APIs, and external endpoint routing. They’re intentionally minimal rather than full load balancing configurations.
The examples may reference pre-existing infrastructure such as Cloud Run services, Cloud Functions, App Engine versions, VPC networks and subnetworks, and Service Attachments for PSC. They focus on configuring the NEG rather than provisioning the complete load balancing stack.
To keep things focused, common NEG patterns are omitted, including:
- Port mapping for GCE VM instances (GCE_VM_IP_PORTMAP)
- PSC Service Attachment configuration with pscData
- Subnetwork specification for PSC NEGs
- Network endpoint management and health checks
These omissions are intentional: the goal is to illustrate how each NEG type is wired, not provide drop-in load balancing modules. See the RegionNetworkEndpointGroup resource reference for all available configuration options.
Let's configure GCP Regional Network Endpoint Groups
Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.
Try Pulumi Cloud for FREEFrequently Asked Questions
Configuration & NEG Types
networkEndpointType: SERVERLESS (default, for Cloud Functions/Run/App Engine), PRIVATE_SERVICE_CONNECT (for PSC connections), INTERNET_IP_PORT and INTERNET_FQDN_PORT (for external backends), and GCE_VM_IP_PORTMAP (for port mapping).appEngine, cloudFunction, cloudRun, and serverlessDeployment only work with SERVERLESS NEGs. pscData and subnetwork only work with PRIVATE_SERVICE_CONNECT NEGs. network and pscTargetService work with both PRIVATE_SERVICE_CONNECT and INTERNET NEGs.network property defaults to the “default” project network when unspecified. This applies to PSC and INTERNET NEGs.Serverless Backends
cloudRun, appEngine, cloudFunction, or serverlessDeployment) simultaneously causes a configuration error.networkEndpointType to SERVERLESS and configure cloudFunction with the function name, as shown in the Cloud Functions example.networkEndpointType to SERVERLESS and configure cloudRun with the service name, as shown in the Cloud Run example.networkEndpointType to SERVERLESS and configure appEngine with the service and version. You can also leave appEngine empty to use the default service.Private Service Connect
networkEndpointType to PRIVATE_SERVICE_CONNECT and specify pscTargetService with either a Google API URL (like asia-northeast3-cloudkms.googleapis.com) or a service attachment self-link. For service attachments, you can also configure pscData with a producer port and specify a subnetwork.Immutability & Lifecycle
name, network, region, networkEndpointType, appEngine, cloudFunction, cloudRun, pscData, pscTargetService, serverlessDeployment, subnetwork, and description. Changes to these properties force resource recreation.name must be 1-63 characters long and match the regex a-z?, meaning it starts with a lowercase letter, followed by dashes, lowercase letters, or digits, and cannot end with a dash. This complies with RFC1035.Using a different cloud?
Explore networking guides for other cloud providers: