Deploy GCP Cloud Run Jobs

The gcp:cloudrunv2/job:Job resource, part of the Pulumi GCP provider, defines a Cloud Run Job that runs containerized tasks to completion on demand or on schedule. This guide focuses on three capabilities: container image deployment, resource limits and VPC connectivity, and secrets and volume management.

Jobs reference container images from registries and may connect to Cloud SQL, Secret Manager, or VPC resources that must exist separately. The examples are intentionally small. Combine them with your own container images, IAM policies, and networking infrastructure.

Deploy a job with a container image

Most deployments start by specifying a container image and location. The job resource defines what to run but doesn’t execute until triggered.

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

const _default = new gcp.cloudrunv2.Job("default", {
    name: "cloudrun-job",
    location: "us-central1",
    deletionProtection: false,
    template: {
        template: {
            containers: [{
                image: "us-docker.pkg.dev/cloudrun/container/job",
            }],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.cloudrunv2.Job("default",
    name="cloudrun-job",
    location="us-central1",
    deletion_protection=False,
    template={
        "template": {
            "containers": [{
                "image": "us-docker.pkg.dev/cloudrun/container/job",
            }],
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cloudrunv2.NewJob(ctx, "default", &cloudrunv2.JobArgs{
			Name:               pulumi.String("cloudrun-job"),
			Location:           pulumi.String("us-central1"),
			DeletionProtection: pulumi.Bool(false),
			Template: &cloudrunv2.JobTemplateArgs{
				Template: &cloudrunv2.JobTemplateTemplateArgs{
					Containers: cloudrunv2.JobTemplateTemplateContainerArray{
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
						},
					},
				},
			},
		})
		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.CloudRunV2.Job("default", new()
    {
        Name = "cloudrun-job",
        Location = "us-central1",
        DeletionProtection = false,
        Template = new Gcp.CloudRunV2.Inputs.JobTemplateArgs
        {
            Template = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateArgs
            {
                Containers = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudrunv2.Job;
import com.pulumi.gcp.cloudrunv2.JobArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateArgs;
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 Job("default", JobArgs.builder()
            .name("cloudrun-job")
            .location("us-central1")
            .deletionProtection(false)
            .template(JobTemplateArgs.builder()
                .template(JobTemplateTemplateArgs.builder()
                    .containers(JobTemplateTemplateContainerArgs.builder()
                        .image("us-docker.pkg.dev/cloudrun/container/job")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:cloudrunv2:Job
    properties:
      name: cloudrun-job
      location: us-central1
      deletionProtection: false
      template:
        template:
          containers:
            - image: us-docker.pkg.dev/cloudrun/container/job

The template property defines the execution environment. Inside template.template.containers, the image property points to your container in Artifact Registry or Container Registry. The job remains idle until you trigger an execution manually or via Cloud Scheduler.

Set CPU and memory resource limits

Jobs that process large datasets or perform compute-intensive work need explicit resource allocation for predictable performance.

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

const _default = new gcp.cloudrunv2.Job("default", {
    name: "cloudrun-job",
    location: "us-central1",
    deletionProtection: false,
    template: {
        template: {
            containers: [{
                image: "us-docker.pkg.dev/cloudrun/container/job",
                resources: {
                    limits: {
                        cpu: "2",
                        memory: "1024Mi",
                    },
                },
            }],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.cloudrunv2.Job("default",
    name="cloudrun-job",
    location="us-central1",
    deletion_protection=False,
    template={
        "template": {
            "containers": [{
                "image": "us-docker.pkg.dev/cloudrun/container/job",
                "resources": {
                    "limits": {
                        "cpu": "2",
                        "memory": "1024Mi",
                    },
                },
            }],
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cloudrunv2.NewJob(ctx, "default", &cloudrunv2.JobArgs{
			Name:               pulumi.String("cloudrun-job"),
			Location:           pulumi.String("us-central1"),
			DeletionProtection: pulumi.Bool(false),
			Template: &cloudrunv2.JobTemplateArgs{
				Template: &cloudrunv2.JobTemplateTemplateArgs{
					Containers: cloudrunv2.JobTemplateTemplateContainerArray{
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
							Resources: &cloudrunv2.JobTemplateTemplateContainerResourcesArgs{
								Limits: pulumi.StringMap{
									"cpu":    pulumi.String("2"),
									"memory": pulumi.String("1024Mi"),
								},
							},
						},
					},
				},
			},
		})
		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.CloudRunV2.Job("default", new()
    {
        Name = "cloudrun-job",
        Location = "us-central1",
        DeletionProtection = false,
        Template = new Gcp.CloudRunV2.Inputs.JobTemplateArgs
        {
            Template = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateArgs
            {
                Containers = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                        Resources = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerResourcesArgs
                        {
                            Limits = 
                            {
                                { "cpu", "2" },
                                { "memory", "1024Mi" },
                            },
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudrunv2.Job;
import com.pulumi.gcp.cloudrunv2.JobArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateArgs;
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 Job("default", JobArgs.builder()
            .name("cloudrun-job")
            .location("us-central1")
            .deletionProtection(false)
            .template(JobTemplateArgs.builder()
                .template(JobTemplateTemplateArgs.builder()
                    .containers(JobTemplateTemplateContainerArgs.builder()
                        .image("us-docker.pkg.dev/cloudrun/container/job")
                        .resources(JobTemplateTemplateContainerResourcesArgs.builder()
                            .limits(Map.ofEntries(
                                Map.entry("cpu", "2"),
                                Map.entry("memory", "1024Mi")
                            ))
                            .build())
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:cloudrunv2:Job
    properties:
      name: cloudrun-job
      location: us-central1
      deletionProtection: false
      template:
        template:
          containers:
            - image: us-docker.pkg.dev/cloudrun/container/job
              resources:
                limits:
                  cpu: '2'
                  memory: 1024Mi

The resources.limits block controls CPU cores and memory allocated to each task execution. CPU is specified as a string (e.g., “2” for 2 cores), and memory uses units like “1024Mi” for 1 GiB. These limits affect both performance and cost per execution.

Connect to Cloud SQL with secrets

Jobs that query databases need Cloud SQL connectivity and credentials passed securely through Secret Manager.

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

const secret = new gcp.secretmanager.Secret("secret", {
    secretId: "secret",
    replication: {
        auto: {},
    },
});
const instance = new gcp.sql.DatabaseInstance("instance", {
    name: "cloudrun-sql",
    region: "us-central1",
    databaseVersion: "MYSQL_5_7",
    settings: {
        tier: "db-f1-micro",
    },
    deletionProtection: true,
});
const _default = new gcp.cloudrunv2.Job("default", {
    name: "cloudrun-job",
    location: "us-central1",
    deletionProtection: false,
    template: {
        template: {
            volumes: [{
                name: "cloudsql",
                cloudSqlInstance: {
                    instances: [instance.connectionName],
                },
            }],
            containers: [{
                image: "us-docker.pkg.dev/cloudrun/container/job",
                envs: [
                    {
                        name: "FOO",
                        value: "bar",
                    },
                    {
                        name: "latestdclsecret",
                        valueSource: {
                            secretKeyRef: {
                                secret: secret.secretId,
                                version: "1",
                            },
                        },
                    },
                ],
                volumeMounts: [{
                    name: "cloudsql",
                    mountPath: "/cloudsql",
                }],
            }],
        },
    },
});
const project = gcp.organizations.getProject({});
const secret_version_data = new gcp.secretmanager.SecretVersion("secret-version-data", {
    secret: secret.name,
    secretData: "secret-data",
});
const secret_access = new gcp.secretmanager.SecretIamMember("secret-access", {
    secretId: secret.id,
    role: "roles/secretmanager.secretAccessor",
    member: project.then(project => `serviceAccount:${project.number}-compute@developer.gserviceaccount.com`),
}, {
    dependsOn: [secret],
});
import pulumi
import pulumi_gcp as gcp

secret = gcp.secretmanager.Secret("secret",
    secret_id="secret",
    replication={
        "auto": {},
    })
instance = gcp.sql.DatabaseInstance("instance",
    name="cloudrun-sql",
    region="us-central1",
    database_version="MYSQL_5_7",
    settings={
        "tier": "db-f1-micro",
    },
    deletion_protection=True)
default = gcp.cloudrunv2.Job("default",
    name="cloudrun-job",
    location="us-central1",
    deletion_protection=False,
    template={
        "template": {
            "volumes": [{
                "name": "cloudsql",
                "cloud_sql_instance": {
                    "instances": [instance.connection_name],
                },
            }],
            "containers": [{
                "image": "us-docker.pkg.dev/cloudrun/container/job",
                "envs": [
                    {
                        "name": "FOO",
                        "value": "bar",
                    },
                    {
                        "name": "latestdclsecret",
                        "value_source": {
                            "secret_key_ref": {
                                "secret": secret.secret_id,
                                "version": "1",
                            },
                        },
                    },
                ],
                "volume_mounts": [{
                    "name": "cloudsql",
                    "mount_path": "/cloudsql",
                }],
            }],
        },
    })
project = gcp.organizations.get_project()
secret_version_data = gcp.secretmanager.SecretVersion("secret-version-data",
    secret=secret.name,
    secret_data="secret-data")
secret_access = gcp.secretmanager.SecretIamMember("secret-access",
    secret_id=secret.id,
    role="roles/secretmanager.secretAccessor",
    member=f"serviceAccount:{project.number}-compute@developer.gserviceaccount.com",
    opts = pulumi.ResourceOptions(depends_on=[secret]))
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudrunv2"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/sql"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		secret, err := secretmanager.NewSecret(ctx, "secret", &secretmanager.SecretArgs{
			SecretId: pulumi.String("secret"),
			Replication: &secretmanager.SecretReplicationArgs{
				Auto: &secretmanager.SecretReplicationAutoArgs{},
			},
		})
		if err != nil {
			return err
		}
		instance, err := sql.NewDatabaseInstance(ctx, "instance", &sql.DatabaseInstanceArgs{
			Name:            pulumi.String("cloudrun-sql"),
			Region:          pulumi.String("us-central1"),
			DatabaseVersion: pulumi.String("MYSQL_5_7"),
			Settings: &sql.DatabaseInstanceSettingsArgs{
				Tier: pulumi.String("db-f1-micro"),
			},
			DeletionProtection: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		_, err = cloudrunv2.NewJob(ctx, "default", &cloudrunv2.JobArgs{
			Name:               pulumi.String("cloudrun-job"),
			Location:           pulumi.String("us-central1"),
			DeletionProtection: pulumi.Bool(false),
			Template: &cloudrunv2.JobTemplateArgs{
				Template: &cloudrunv2.JobTemplateTemplateArgs{
					Volumes: cloudrunv2.JobTemplateTemplateVolumeArray{
						&cloudrunv2.JobTemplateTemplateVolumeArgs{
							Name: pulumi.String("cloudsql"),
							CloudSqlInstance: &cloudrunv2.JobTemplateTemplateVolumeCloudSqlInstanceArgs{
								Instances: pulumi.StringArray{
									instance.ConnectionName,
								},
							},
						},
					},
					Containers: cloudrunv2.JobTemplateTemplateContainerArray{
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
							Envs: cloudrunv2.JobTemplateTemplateContainerEnvArray{
								&cloudrunv2.JobTemplateTemplateContainerEnvArgs{
									Name:  pulumi.String("FOO"),
									Value: pulumi.String("bar"),
								},
								&cloudrunv2.JobTemplateTemplateContainerEnvArgs{
									Name: pulumi.String("latestdclsecret"),
									ValueSource: &cloudrunv2.JobTemplateTemplateContainerEnvValueSourceArgs{
										SecretKeyRef: &cloudrunv2.JobTemplateTemplateContainerEnvValueSourceSecretKeyRefArgs{
											Secret:  secret.SecretId,
											Version: pulumi.String("1"),
										},
									},
								},
							},
							VolumeMounts: cloudrunv2.JobTemplateTemplateContainerVolumeMountArray{
								&cloudrunv2.JobTemplateTemplateContainerVolumeMountArgs{
									Name:      pulumi.String("cloudsql"),
									MountPath: pulumi.String("/cloudsql"),
								},
							},
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		_, err = secretmanager.NewSecretVersion(ctx, "secret-version-data", &secretmanager.SecretVersionArgs{
			Secret:     secret.Name,
			SecretData: pulumi.String("secret-data"),
		})
		if err != nil {
			return err
		}
		_, err = secretmanager.NewSecretIamMember(ctx, "secret-access", &secretmanager.SecretIamMemberArgs{
			SecretId: secret.ID(),
			Role:     pulumi.String("roles/secretmanager.secretAccessor"),
			Member:   pulumi.Sprintf("serviceAccount:%v-compute@developer.gserviceaccount.com", project.Number),
		}, pulumi.DependsOn([]pulumi.Resource{
			secret,
		}))
		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 secret = new Gcp.SecretManager.Secret("secret", new()
    {
        SecretId = "secret",
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            Auto = null,
        },
    });

    var instance = new Gcp.Sql.DatabaseInstance("instance", new()
    {
        Name = "cloudrun-sql",
        Region = "us-central1",
        DatabaseVersion = "MYSQL_5_7",
        Settings = new Gcp.Sql.Inputs.DatabaseInstanceSettingsArgs
        {
            Tier = "db-f1-micro",
        },
        DeletionProtection = true,
    });

    var @default = new Gcp.CloudRunV2.Job("default", new()
    {
        Name = "cloudrun-job",
        Location = "us-central1",
        DeletionProtection = false,
        Template = new Gcp.CloudRunV2.Inputs.JobTemplateArgs
        {
            Template = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateArgs
            {
                Volumes = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVolumeArgs
                    {
                        Name = "cloudsql",
                        CloudSqlInstance = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVolumeCloudSqlInstanceArgs
                        {
                            Instances = new[]
                            {
                                instance.ConnectionName,
                            },
                        },
                    },
                },
                Containers = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                        Envs = new[]
                        {
                            new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerEnvArgs
                            {
                                Name = "FOO",
                                Value = "bar",
                            },
                            new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerEnvArgs
                            {
                                Name = "latestdclsecret",
                                ValueSource = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerEnvValueSourceArgs
                                {
                                    SecretKeyRef = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerEnvValueSourceSecretKeyRefArgs
                                    {
                                        Secret = secret.SecretId,
                                        Version = "1",
                                    },
                                },
                            },
                        },
                        VolumeMounts = new[]
                        {
                            new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerVolumeMountArgs
                            {
                                Name = "cloudsql",
                                MountPath = "/cloudsql",
                            },
                        },
                    },
                },
            },
        },
    });

    var project = Gcp.Organizations.GetProject.Invoke();

    var secret_version_data = new Gcp.SecretManager.SecretVersion("secret-version-data", new()
    {
        Secret = secret.Name,
        SecretData = "secret-data",
    });

    var secret_access = new Gcp.SecretManager.SecretIamMember("secret-access", new()
    {
        SecretId = secret.Id,
        Role = "roles/secretmanager.secretAccessor",
        Member = $"serviceAccount:{project.Apply(getProjectResult => getProjectResult.Number)}-compute@developer.gserviceaccount.com",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            secret,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.Secret;
import com.pulumi.gcp.secretmanager.SecretArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationAutoArgs;
import com.pulumi.gcp.sql.DatabaseInstance;
import com.pulumi.gcp.sql.DatabaseInstanceArgs;
import com.pulumi.gcp.sql.inputs.DatabaseInstanceSettingsArgs;
import com.pulumi.gcp.cloudrunv2.Job;
import com.pulumi.gcp.cloudrunv2.JobArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import com.pulumi.gcp.secretmanager.SecretIamMember;
import com.pulumi.gcp.secretmanager.SecretIamMemberArgs;
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 secret = new Secret("secret", SecretArgs.builder()
            .secretId("secret")
            .replication(SecretReplicationArgs.builder()
                .auto(SecretReplicationAutoArgs.builder()
                    .build())
                .build())
            .build());

        var instance = new DatabaseInstance("instance", DatabaseInstanceArgs.builder()
            .name("cloudrun-sql")
            .region("us-central1")
            .databaseVersion("MYSQL_5_7")
            .settings(DatabaseInstanceSettingsArgs.builder()
                .tier("db-f1-micro")
                .build())
            .deletionProtection(true)
            .build());

        var default_ = new Job("default", JobArgs.builder()
            .name("cloudrun-job")
            .location("us-central1")
            .deletionProtection(false)
            .template(JobTemplateArgs.builder()
                .template(JobTemplateTemplateArgs.builder()
                    .volumes(JobTemplateTemplateVolumeArgs.builder()
                        .name("cloudsql")
                        .cloudSqlInstance(JobTemplateTemplateVolumeCloudSqlInstanceArgs.builder()
                            .instances(instance.connectionName())
                            .build())
                        .build())
                    .containers(JobTemplateTemplateContainerArgs.builder()
                        .image("us-docker.pkg.dev/cloudrun/container/job")
                        .envs(                        
                            JobTemplateTemplateContainerEnvArgs.builder()
                                .name("FOO")
                                .value("bar")
                                .build(),
                            JobTemplateTemplateContainerEnvArgs.builder()
                                .name("latestdclsecret")
                                .valueSource(JobTemplateTemplateContainerEnvValueSourceArgs.builder()
                                    .secretKeyRef(JobTemplateTemplateContainerEnvValueSourceSecretKeyRefArgs.builder()
                                        .secret(secret.secretId())
                                        .version("1")
                                        .build())
                                    .build())
                                .build())
                        .volumeMounts(JobTemplateTemplateContainerVolumeMountArgs.builder()
                            .name("cloudsql")
                            .mountPath("/cloudsql")
                            .build())
                        .build())
                    .build())
                .build())
            .build());

        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var secret_version_data = new SecretVersion("secret-version-data", SecretVersionArgs.builder()
            .secret(secret.name())
            .secretData("secret-data")
            .build());

        var secret_access = new SecretIamMember("secret-access", SecretIamMemberArgs.builder()
            .secretId(secret.id())
            .role("roles/secretmanager.secretAccessor")
            .member(String.format("serviceAccount:%s-compute@developer.gserviceaccount.com", project.number()))
            .build(), CustomResourceOptions.builder()
                .dependsOn(secret)
                .build());

    }
}
resources:
  default:
    type: gcp:cloudrunv2:Job
    properties:
      name: cloudrun-job
      location: us-central1
      deletionProtection: false
      template:
        template:
          volumes:
            - name: cloudsql
              cloudSqlInstance:
                instances:
                  - ${instance.connectionName}
          containers:
            - image: us-docker.pkg.dev/cloudrun/container/job
              envs:
                - name: FOO
                  value: bar
                - name: latestdclsecret
                  valueSource:
                    secretKeyRef:
                      secret: ${secret.secretId}
                      version: '1'
              volumeMounts:
                - name: cloudsql
                  mountPath: /cloudsql
  secret:
    type: gcp:secretmanager:Secret
    properties:
      secretId: secret
      replication:
        auto: {}
  secret-version-data:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${secret.name}
      secretData: secret-data
  secret-access:
    type: gcp:secretmanager:SecretIamMember
    properties:
      secretId: ${secret.id}
      role: roles/secretmanager.secretAccessor
      member: serviceAccount:${project.number}-compute@developer.gserviceaccount.com
    options:
      dependsOn:
        - ${secret}
  instance:
    type: gcp:sql:DatabaseInstance
    properties:
      name: cloudrun-sql
      region: us-central1
      databaseVersion: MYSQL_5_7
      settings:
        tier: db-f1-micro
      deletionProtection: true
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

Cloud SQL instances are mounted as Unix sockets via volumes.cloudSqlInstance. The volumeMounts property attaches the socket at /cloudsql, where your application connects using the instance connection name. Secrets are injected as environment variables through envs.valueSource.secretKeyRef, which references a Secret Manager secret by ID and version. The compute service account needs the secretmanager.secretAccessor role to read secrets.

Route traffic through VPC Access Connector

Jobs that need to reach internal services or databases in a VPC use Serverless VPC Access connectors.

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

const customTestNetwork = new gcp.compute.Network("custom_test", {
    name: "run-network",
    autoCreateSubnetworks: false,
});
const customTest = new gcp.compute.Subnetwork("custom_test", {
    name: "run-subnetwork",
    ipCidrRange: "10.2.0.0/28",
    region: "us-central1",
    network: customTestNetwork.id,
});
const connector = new gcp.vpcaccess.Connector("connector", {
    name: "run-vpc",
    subnet: {
        name: customTest.name,
    },
    machineType: "e2-standard-4",
    minInstances: 2,
    maxInstances: 3,
    region: "us-central1",
});
const _default = new gcp.cloudrunv2.Job("default", {
    name: "cloudrun-job",
    location: "us-central1",
    deletionProtection: false,
    template: {
        template: {
            containers: [{
                image: "us-docker.pkg.dev/cloudrun/container/job",
            }],
            vpcAccess: {
                connector: connector.id,
                egress: "ALL_TRAFFIC",
            },
        },
    },
});
import pulumi
import pulumi_gcp as gcp

custom_test_network = gcp.compute.Network("custom_test",
    name="run-network",
    auto_create_subnetworks=False)
custom_test = gcp.compute.Subnetwork("custom_test",
    name="run-subnetwork",
    ip_cidr_range="10.2.0.0/28",
    region="us-central1",
    network=custom_test_network.id)
connector = gcp.vpcaccess.Connector("connector",
    name="run-vpc",
    subnet={
        "name": custom_test.name,
    },
    machine_type="e2-standard-4",
    min_instances=2,
    max_instances=3,
    region="us-central1")
default = gcp.cloudrunv2.Job("default",
    name="cloudrun-job",
    location="us-central1",
    deletion_protection=False,
    template={
        "template": {
            "containers": [{
                "image": "us-docker.pkg.dev/cloudrun/container/job",
            }],
            "vpc_access": {
                "connector": connector.id,
                "egress": "ALL_TRAFFIC",
            },
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		customTestNetwork, err := compute.NewNetwork(ctx, "custom_test", &compute.NetworkArgs{
			Name:                  pulumi.String("run-network"),
			AutoCreateSubnetworks: pulumi.Bool(false),
		})
		if err != nil {
			return err
		}
		customTest, err := compute.NewSubnetwork(ctx, "custom_test", &compute.SubnetworkArgs{
			Name:        pulumi.String("run-subnetwork"),
			IpCidrRange: pulumi.String("10.2.0.0/28"),
			Region:      pulumi.String("us-central1"),
			Network:     customTestNetwork.ID(),
		})
		if err != nil {
			return err
		}
		connector, err := vpcaccess.NewConnector(ctx, "connector", &vpcaccess.ConnectorArgs{
			Name: pulumi.String("run-vpc"),
			Subnet: &vpcaccess.ConnectorSubnetArgs{
				Name: customTest.Name,
			},
			MachineType:  pulumi.String("e2-standard-4"),
			MinInstances: pulumi.Int(2),
			MaxInstances: pulumi.Int(3),
			Region:       pulumi.String("us-central1"),
		})
		if err != nil {
			return err
		}
		_, err = cloudrunv2.NewJob(ctx, "default", &cloudrunv2.JobArgs{
			Name:               pulumi.String("cloudrun-job"),
			Location:           pulumi.String("us-central1"),
			DeletionProtection: pulumi.Bool(false),
			Template: &cloudrunv2.JobTemplateArgs{
				Template: &cloudrunv2.JobTemplateTemplateArgs{
					Containers: cloudrunv2.JobTemplateTemplateContainerArray{
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
						},
					},
					VpcAccess: &cloudrunv2.JobTemplateTemplateVpcAccessArgs{
						Connector: connector.ID(),
						Egress:    pulumi.String("ALL_TRAFFIC"),
					},
				},
			},
		})
		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 customTestNetwork = new Gcp.Compute.Network("custom_test", new()
    {
        Name = "run-network",
        AutoCreateSubnetworks = false,
    });

    var customTest = new Gcp.Compute.Subnetwork("custom_test", new()
    {
        Name = "run-subnetwork",
        IpCidrRange = "10.2.0.0/28",
        Region = "us-central1",
        Network = customTestNetwork.Id,
    });

    var connector = new Gcp.VpcAccess.Connector("connector", new()
    {
        Name = "run-vpc",
        Subnet = new Gcp.VpcAccess.Inputs.ConnectorSubnetArgs
        {
            Name = customTest.Name,
        },
        MachineType = "e2-standard-4",
        MinInstances = 2,
        MaxInstances = 3,
        Region = "us-central1",
    });

    var @default = new Gcp.CloudRunV2.Job("default", new()
    {
        Name = "cloudrun-job",
        Location = "us-central1",
        DeletionProtection = false,
        Template = new Gcp.CloudRunV2.Inputs.JobTemplateArgs
        {
            Template = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateArgs
            {
                Containers = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                    },
                },
                VpcAccess = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVpcAccessArgs
                {
                    Connector = connector.Id,
                    Egress = "ALL_TRAFFIC",
                },
            },
        },
    });

});
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.vpcaccess.Connector;
import com.pulumi.gcp.vpcaccess.ConnectorArgs;
import com.pulumi.gcp.vpcaccess.inputs.ConnectorSubnetArgs;
import com.pulumi.gcp.cloudrunv2.Job;
import com.pulumi.gcp.cloudrunv2.JobArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateVpcAccessArgs;
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 customTestNetwork = new Network("customTestNetwork", NetworkArgs.builder()
            .name("run-network")
            .autoCreateSubnetworks(false)
            .build());

        var customTest = new Subnetwork("customTest", SubnetworkArgs.builder()
            .name("run-subnetwork")
            .ipCidrRange("10.2.0.0/28")
            .region("us-central1")
            .network(customTestNetwork.id())
            .build());

        var connector = new Connector("connector", ConnectorArgs.builder()
            .name("run-vpc")
            .subnet(ConnectorSubnetArgs.builder()
                .name(customTest.name())
                .build())
            .machineType("e2-standard-4")
            .minInstances(2)
            .maxInstances(3)
            .region("us-central1")
            .build());

        var default_ = new Job("default", JobArgs.builder()
            .name("cloudrun-job")
            .location("us-central1")
            .deletionProtection(false)
            .template(JobTemplateArgs.builder()
                .template(JobTemplateTemplateArgs.builder()
                    .containers(JobTemplateTemplateContainerArgs.builder()
                        .image("us-docker.pkg.dev/cloudrun/container/job")
                        .build())
                    .vpcAccess(JobTemplateTemplateVpcAccessArgs.builder()
                        .connector(connector.id())
                        .egress("ALL_TRAFFIC")
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:cloudrunv2:Job
    properties:
      name: cloudrun-job
      location: us-central1
      deletionProtection: false
      template:
        template:
          containers:
            - image: us-docker.pkg.dev/cloudrun/container/job
          vpcAccess:
            connector: ${connector.id}
            egress: ALL_TRAFFIC
  connector:
    type: gcp:vpcaccess:Connector
    properties:
      name: run-vpc
      subnet:
        name: ${customTest.name}
      machineType: e2-standard-4
      minInstances: 2
      maxInstances: 3
      region: us-central1
  customTest:
    type: gcp:compute:Subnetwork
    name: custom_test
    properties:
      name: run-subnetwork
      ipCidrRange: 10.2.0.0/28
      region: us-central1
      network: ${customTestNetwork.id}
  customTestNetwork:
    type: gcp:compute:Network
    name: custom_test
    properties:
      name: run-network
      autoCreateSubnetworks: false

The vpcAccess.connector property references a VPC Access Connector by ID. The egress property controls which traffic routes through the connector: “ALL_TRAFFIC” sends all outbound requests through the VPC, while “PRIVATE_RANGES_ONLY” routes only RFC 1918 addresses. This enables access to private Cloud SQL instances, internal load balancers, and other VPC resources.

Connect directly to VPC with network interfaces

Direct VPC egress bypasses VPC Access connectors by attaching network interfaces directly to the job, reducing latency and cost.

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

const _default = new gcp.cloudrunv2.Job("default", {
    name: "cloudrun-job",
    location: "us-central1",
    deletionProtection: false,
    launchStage: "GA",
    template: {
        template: {
            containers: [{
                image: "us-docker.pkg.dev/cloudrun/container/job",
            }],
            vpcAccess: {
                networkInterfaces: [{
                    network: "default",
                    subnetwork: "default",
                    tags: [
                        "tag1",
                        "tag2",
                        "tag3",
                    ],
                }],
            },
        },
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.cloudrunv2.Job("default",
    name="cloudrun-job",
    location="us-central1",
    deletion_protection=False,
    launch_stage="GA",
    template={
        "template": {
            "containers": [{
                "image": "us-docker.pkg.dev/cloudrun/container/job",
            }],
            "vpc_access": {
                "network_interfaces": [{
                    "network": "default",
                    "subnetwork": "default",
                    "tags": [
                        "tag1",
                        "tag2",
                        "tag3",
                    ],
                }],
            },
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cloudrunv2.NewJob(ctx, "default", &cloudrunv2.JobArgs{
			Name:               pulumi.String("cloudrun-job"),
			Location:           pulumi.String("us-central1"),
			DeletionProtection: pulumi.Bool(false),
			LaunchStage:        pulumi.String("GA"),
			Template: &cloudrunv2.JobTemplateArgs{
				Template: &cloudrunv2.JobTemplateTemplateArgs{
					Containers: cloudrunv2.JobTemplateTemplateContainerArray{
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
						},
					},
					VpcAccess: &cloudrunv2.JobTemplateTemplateVpcAccessArgs{
						NetworkInterfaces: cloudrunv2.JobTemplateTemplateVpcAccessNetworkInterfaceArray{
							&cloudrunv2.JobTemplateTemplateVpcAccessNetworkInterfaceArgs{
								Network:    pulumi.String("default"),
								Subnetwork: pulumi.String("default"),
								Tags: pulumi.StringArray{
									pulumi.String("tag1"),
									pulumi.String("tag2"),
									pulumi.String("tag3"),
								},
							},
						},
					},
				},
			},
		})
		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.CloudRunV2.Job("default", new()
    {
        Name = "cloudrun-job",
        Location = "us-central1",
        DeletionProtection = false,
        LaunchStage = "GA",
        Template = new Gcp.CloudRunV2.Inputs.JobTemplateArgs
        {
            Template = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateArgs
            {
                Containers = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                    },
                },
                VpcAccess = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVpcAccessArgs
                {
                    NetworkInterfaces = new[]
                    {
                        new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVpcAccessNetworkInterfaceArgs
                        {
                            Network = "default",
                            Subnetwork = "default",
                            Tags = new[]
                            {
                                "tag1",
                                "tag2",
                                "tag3",
                            },
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudrunv2.Job;
import com.pulumi.gcp.cloudrunv2.JobArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateVpcAccessArgs;
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 Job("default", JobArgs.builder()
            .name("cloudrun-job")
            .location("us-central1")
            .deletionProtection(false)
            .launchStage("GA")
            .template(JobTemplateArgs.builder()
                .template(JobTemplateTemplateArgs.builder()
                    .containers(JobTemplateTemplateContainerArgs.builder()
                        .image("us-docker.pkg.dev/cloudrun/container/job")
                        .build())
                    .vpcAccess(JobTemplateTemplateVpcAccessArgs.builder()
                        .networkInterfaces(JobTemplateTemplateVpcAccessNetworkInterfaceArgs.builder()
                            .network("default")
                            .subnetwork("default")
                            .tags(                            
                                "tag1",
                                "tag2",
                                "tag3")
                            .build())
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:cloudrunv2:Job
    properties:
      name: cloudrun-job
      location: us-central1
      deletionProtection: false
      launchStage: GA
      template:
        template:
          containers:
            - image: us-docker.pkg.dev/cloudrun/container/job
          vpcAccess:
            networkInterfaces:
              - network: default
                subnetwork: default
                tags:
                  - tag1
                  - tag2
                  - tag3

The vpcAccess.networkInterfaces property attaches the job directly to a VPC network and subnet. The tags property applies network tags for firewall rules. Direct VPC is generally preferred over VPC Access connectors for lower latency and simpler configuration, but requires the job to be in GA launch stage.

Mount secrets as files in the container

Applications that read configuration from files can consume Secret Manager secrets mounted as volumes.

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

const secret = new gcp.secretmanager.Secret("secret", {
    secretId: "secret",
    replication: {
        auto: {},
    },
});
const secret_version_data = new gcp.secretmanager.SecretVersion("secret-version-data", {
    secret: secret.name,
    secretData: "secret-data",
});
const project = gcp.organizations.getProject({});
const secret_access = new gcp.secretmanager.SecretIamMember("secret-access", {
    secretId: secret.id,
    role: "roles/secretmanager.secretAccessor",
    member: project.then(project => `serviceAccount:${project.number}-compute@developer.gserviceaccount.com`),
}, {
    dependsOn: [secret],
});
const _default = new gcp.cloudrunv2.Job("default", {
    name: "cloudrun-job",
    location: "us-central1",
    deletionProtection: false,
    template: {
        template: {
            volumes: [{
                name: "a-volume",
                secret: {
                    secret: secret.secretId,
                    defaultMode: 292,
                    items: [{
                        version: "1",
                        path: "my-secret",
                        mode: 256,
                    }],
                },
            }],
            containers: [{
                image: "us-docker.pkg.dev/cloudrun/container/job",
                volumeMounts: [{
                    name: "a-volume",
                    mountPath: "/secrets",
                }],
            }],
        },
    },
}, {
    dependsOn: [
        secret_version_data,
        secret_access,
    ],
});
import pulumi
import pulumi_gcp as gcp

secret = gcp.secretmanager.Secret("secret",
    secret_id="secret",
    replication={
        "auto": {},
    })
secret_version_data = gcp.secretmanager.SecretVersion("secret-version-data",
    secret=secret.name,
    secret_data="secret-data")
project = gcp.organizations.get_project()
secret_access = gcp.secretmanager.SecretIamMember("secret-access",
    secret_id=secret.id,
    role="roles/secretmanager.secretAccessor",
    member=f"serviceAccount:{project.number}-compute@developer.gserviceaccount.com",
    opts = pulumi.ResourceOptions(depends_on=[secret]))
default = gcp.cloudrunv2.Job("default",
    name="cloudrun-job",
    location="us-central1",
    deletion_protection=False,
    template={
        "template": {
            "volumes": [{
                "name": "a-volume",
                "secret": {
                    "secret": secret.secret_id,
                    "default_mode": 292,
                    "items": [{
                        "version": "1",
                        "path": "my-secret",
                        "mode": 256,
                    }],
                },
            }],
            "containers": [{
                "image": "us-docker.pkg.dev/cloudrun/container/job",
                "volume_mounts": [{
                    "name": "a-volume",
                    "mount_path": "/secrets",
                }],
            }],
        },
    },
    opts = pulumi.ResourceOptions(depends_on=[
            secret_version_data,
            secret_access,
        ]))
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/cloudrunv2"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/secretmanager"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		secret, err := secretmanager.NewSecret(ctx, "secret", &secretmanager.SecretArgs{
			SecretId: pulumi.String("secret"),
			Replication: &secretmanager.SecretReplicationArgs{
				Auto: &secretmanager.SecretReplicationAutoArgs{},
			},
		})
		if err != nil {
			return err
		}
		secret_version_data, err := secretmanager.NewSecretVersion(ctx, "secret-version-data", &secretmanager.SecretVersionArgs{
			Secret:     secret.Name,
			SecretData: pulumi.String("secret-data"),
		})
		if err != nil {
			return err
		}
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		secret_access, err := secretmanager.NewSecretIamMember(ctx, "secret-access", &secretmanager.SecretIamMemberArgs{
			SecretId: secret.ID(),
			Role:     pulumi.String("roles/secretmanager.secretAccessor"),
			Member:   pulumi.Sprintf("serviceAccount:%v-compute@developer.gserviceaccount.com", project.Number),
		}, pulumi.DependsOn([]pulumi.Resource{
			secret,
		}))
		if err != nil {
			return err
		}
		_, err = cloudrunv2.NewJob(ctx, "default", &cloudrunv2.JobArgs{
			Name:               pulumi.String("cloudrun-job"),
			Location:           pulumi.String("us-central1"),
			DeletionProtection: pulumi.Bool(false),
			Template: &cloudrunv2.JobTemplateArgs{
				Template: &cloudrunv2.JobTemplateTemplateArgs{
					Volumes: cloudrunv2.JobTemplateTemplateVolumeArray{
						&cloudrunv2.JobTemplateTemplateVolumeArgs{
							Name: pulumi.String("a-volume"),
							Secret: &cloudrunv2.JobTemplateTemplateVolumeSecretArgs{
								Secret:      secret.SecretId,
								DefaultMode: pulumi.Int(292),
								Items: cloudrunv2.JobTemplateTemplateVolumeSecretItemArray{
									&cloudrunv2.JobTemplateTemplateVolumeSecretItemArgs{
										Version: pulumi.String("1"),
										Path:    pulumi.String("my-secret"),
										Mode:    pulumi.Int(256),
									},
								},
							},
						},
					},
					Containers: cloudrunv2.JobTemplateTemplateContainerArray{
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
							VolumeMounts: cloudrunv2.JobTemplateTemplateContainerVolumeMountArray{
								&cloudrunv2.JobTemplateTemplateContainerVolumeMountArgs{
									Name:      pulumi.String("a-volume"),
									MountPath: pulumi.String("/secrets"),
								},
							},
						},
					},
				},
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			secret_version_data,
			secret_access,
		}))
		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 secret = new Gcp.SecretManager.Secret("secret", new()
    {
        SecretId = "secret",
        Replication = new Gcp.SecretManager.Inputs.SecretReplicationArgs
        {
            Auto = null,
        },
    });

    var secret_version_data = new Gcp.SecretManager.SecretVersion("secret-version-data", new()
    {
        Secret = secret.Name,
        SecretData = "secret-data",
    });

    var project = Gcp.Organizations.GetProject.Invoke();

    var secret_access = new Gcp.SecretManager.SecretIamMember("secret-access", new()
    {
        SecretId = secret.Id,
        Role = "roles/secretmanager.secretAccessor",
        Member = $"serviceAccount:{project.Apply(getProjectResult => getProjectResult.Number)}-compute@developer.gserviceaccount.com",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            secret,
        },
    });

    var @default = new Gcp.CloudRunV2.Job("default", new()
    {
        Name = "cloudrun-job",
        Location = "us-central1",
        DeletionProtection = false,
        Template = new Gcp.CloudRunV2.Inputs.JobTemplateArgs
        {
            Template = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateArgs
            {
                Volumes = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVolumeArgs
                    {
                        Name = "a-volume",
                        Secret = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVolumeSecretArgs
                        {
                            Secret = secret.SecretId,
                            DefaultMode = 292,
                            Items = new[]
                            {
                                new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVolumeSecretItemArgs
                                {
                                    Version = "1",
                                    Path = "my-secret",
                                    Mode = 256,
                                },
                            },
                        },
                    },
                },
                Containers = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                        VolumeMounts = new[]
                        {
                            new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerVolumeMountArgs
                            {
                                Name = "a-volume",
                                MountPath = "/secrets",
                            },
                        },
                    },
                },
            },
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            secret_version_data,
            secret_access,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.secretmanager.Secret;
import com.pulumi.gcp.secretmanager.SecretArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationArgs;
import com.pulumi.gcp.secretmanager.inputs.SecretReplicationAutoArgs;
import com.pulumi.gcp.secretmanager.SecretVersion;
import com.pulumi.gcp.secretmanager.SecretVersionArgs;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.secretmanager.SecretIamMember;
import com.pulumi.gcp.secretmanager.SecretIamMemberArgs;
import com.pulumi.gcp.cloudrunv2.Job;
import com.pulumi.gcp.cloudrunv2.JobArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateArgs;
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 secret = new Secret("secret", SecretArgs.builder()
            .secretId("secret")
            .replication(SecretReplicationArgs.builder()
                .auto(SecretReplicationAutoArgs.builder()
                    .build())
                .build())
            .build());

        var secret_version_data = new SecretVersion("secret-version-data", SecretVersionArgs.builder()
            .secret(secret.name())
            .secretData("secret-data")
            .build());

        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var secret_access = new SecretIamMember("secret-access", SecretIamMemberArgs.builder()
            .secretId(secret.id())
            .role("roles/secretmanager.secretAccessor")
            .member(String.format("serviceAccount:%s-compute@developer.gserviceaccount.com", project.number()))
            .build(), CustomResourceOptions.builder()
                .dependsOn(secret)
                .build());

        var default_ = new Job("default", JobArgs.builder()
            .name("cloudrun-job")
            .location("us-central1")
            .deletionProtection(false)
            .template(JobTemplateArgs.builder()
                .template(JobTemplateTemplateArgs.builder()
                    .volumes(JobTemplateTemplateVolumeArgs.builder()
                        .name("a-volume")
                        .secret(JobTemplateTemplateVolumeSecretArgs.builder()
                            .secret(secret.secretId())
                            .defaultMode(292)
                            .items(JobTemplateTemplateVolumeSecretItemArgs.builder()
                                .version("1")
                                .path("my-secret")
                                .mode(256)
                                .build())
                            .build())
                        .build())
                    .containers(JobTemplateTemplateContainerArgs.builder()
                        .image("us-docker.pkg.dev/cloudrun/container/job")
                        .volumeMounts(JobTemplateTemplateContainerVolumeMountArgs.builder()
                            .name("a-volume")
                            .mountPath("/secrets")
                            .build())
                        .build())
                    .build())
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(                
                    secret_version_data,
                    secret_access)
                .build());

    }
}
resources:
  default:
    type: gcp:cloudrunv2:Job
    properties:
      name: cloudrun-job
      location: us-central1
      deletionProtection: false
      template:
        template:
          volumes:
            - name: a-volume
              secret:
                secret: ${secret.secretId}
                defaultMode: 292
                items:
                  - version: '1'
                    path: my-secret
                    mode: 256
          containers:
            - image: us-docker.pkg.dev/cloudrun/container/job
              volumeMounts:
                - name: a-volume
                  mountPath: /secrets
    options:
      dependsOn:
        - ${["secret-version-data"]}
        - ${["secret-access"]}
  secret:
    type: gcp:secretmanager:Secret
    properties:
      secretId: secret
      replication:
        auto: {}
  secret-version-data:
    type: gcp:secretmanager:SecretVersion
    properties:
      secret: ${secret.name}
      secretData: secret-data
  secret-access:
    type: gcp:secretmanager:SecretIamMember
    properties:
      secretId: ${secret.id}
      role: roles/secretmanager.secretAccessor
      member: serviceAccount:${project.number}-compute@developer.gserviceaccount.com
    options:
      dependsOn:
        - ${secret}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The volumes.secret block mounts a Secret Manager secret as files. The defaultMode sets Unix permissions (292 is octal 0444, read-only). The items array maps secret versions to file paths with custom permissions. The volumeMounts property attaches the volume at /secrets, where your application reads the files. This approach works well for applications that expect configuration files rather than environment variables.

Use ephemeral storage for temporary data

Jobs that need fast temporary storage for intermediate processing can use emptyDir volumes.

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

const _default = new gcp.cloudrunv2.Job("default", {
    name: "cloudrun-job",
    location: "us-central1",
    deletionProtection: false,
    template: {
        template: {
            containers: [{
                image: "us-docker.pkg.dev/cloudrun/container/job",
                volumeMounts: [{
                    name: "empty-dir-volume",
                    mountPath: "/mnt",
                }],
            }],
            volumes: [{
                name: "empty-dir-volume",
                emptyDir: {
                    medium: "MEMORY",
                    sizeLimit: "128Mi",
                },
            }],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.cloudrunv2.Job("default",
    name="cloudrun-job",
    location="us-central1",
    deletion_protection=False,
    template={
        "template": {
            "containers": [{
                "image": "us-docker.pkg.dev/cloudrun/container/job",
                "volume_mounts": [{
                    "name": "empty-dir-volume",
                    "mount_path": "/mnt",
                }],
            }],
            "volumes": [{
                "name": "empty-dir-volume",
                "empty_dir": {
                    "medium": "MEMORY",
                    "size_limit": "128Mi",
                },
            }],
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cloudrunv2.NewJob(ctx, "default", &cloudrunv2.JobArgs{
			Name:               pulumi.String("cloudrun-job"),
			Location:           pulumi.String("us-central1"),
			DeletionProtection: pulumi.Bool(false),
			Template: &cloudrunv2.JobTemplateArgs{
				Template: &cloudrunv2.JobTemplateTemplateArgs{
					Containers: cloudrunv2.JobTemplateTemplateContainerArray{
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
							VolumeMounts: cloudrunv2.JobTemplateTemplateContainerVolumeMountArray{
								&cloudrunv2.JobTemplateTemplateContainerVolumeMountArgs{
									Name:      pulumi.String("empty-dir-volume"),
									MountPath: pulumi.String("/mnt"),
								},
							},
						},
					},
					Volumes: cloudrunv2.JobTemplateTemplateVolumeArray{
						&cloudrunv2.JobTemplateTemplateVolumeArgs{
							Name: pulumi.String("empty-dir-volume"),
							EmptyDir: &cloudrunv2.JobTemplateTemplateVolumeEmptyDirArgs{
								Medium:    pulumi.String("MEMORY"),
								SizeLimit: pulumi.String("128Mi"),
							},
						},
					},
				},
			},
		})
		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.CloudRunV2.Job("default", new()
    {
        Name = "cloudrun-job",
        Location = "us-central1",
        DeletionProtection = false,
        Template = new Gcp.CloudRunV2.Inputs.JobTemplateArgs
        {
            Template = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateArgs
            {
                Containers = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                        VolumeMounts = new[]
                        {
                            new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerVolumeMountArgs
                            {
                                Name = "empty-dir-volume",
                                MountPath = "/mnt",
                            },
                        },
                    },
                },
                Volumes = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVolumeArgs
                    {
                        Name = "empty-dir-volume",
                        EmptyDir = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateVolumeEmptyDirArgs
                        {
                            Medium = "MEMORY",
                            SizeLimit = "128Mi",
                        },
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudrunv2.Job;
import com.pulumi.gcp.cloudrunv2.JobArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateArgs;
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 Job("default", JobArgs.builder()
            .name("cloudrun-job")
            .location("us-central1")
            .deletionProtection(false)
            .template(JobTemplateArgs.builder()
                .template(JobTemplateTemplateArgs.builder()
                    .containers(JobTemplateTemplateContainerArgs.builder()
                        .image("us-docker.pkg.dev/cloudrun/container/job")
                        .volumeMounts(JobTemplateTemplateContainerVolumeMountArgs.builder()
                            .name("empty-dir-volume")
                            .mountPath("/mnt")
                            .build())
                        .build())
                    .volumes(JobTemplateTemplateVolumeArgs.builder()
                        .name("empty-dir-volume")
                        .emptyDir(JobTemplateTemplateVolumeEmptyDirArgs.builder()
                            .medium("MEMORY")
                            .sizeLimit("128Mi")
                            .build())
                        .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:cloudrunv2:Job
    properties:
      name: cloudrun-job
      location: us-central1
      deletionProtection: false
      template:
        template:
          containers:
            - image: us-docker.pkg.dev/cloudrun/container/job
              volumeMounts:
                - name: empty-dir-volume
                  mountPath: /mnt
          volumes:
            - name: empty-dir-volume
              emptyDir:
                medium: MEMORY
                sizeLimit: 128Mi

The volumes.emptyDir block creates ephemeral storage that exists only during job execution. The medium property controls backing storage: “MEMORY” uses RAM for fastest access, while “DISK” uses local SSD. The sizeLimit property caps storage size (e.g., “128Mi” for 128 MiB). Data in emptyDir volumes is lost when the job completes.

Run multiple containers in a single job

Complex workflows can split responsibilities across multiple containers that run in parallel.

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

const _default = new gcp.cloudrunv2.Job("default", {
    name: "cloudrun-job",
    location: "us-central1",
    deletionProtection: false,
    template: {
        template: {
            containers: [
                {
                    name: "job-1",
                    image: "us-docker.pkg.dev/cloudrun/container/job",
                },
                {
                    name: "job-2",
                    image: "us-docker.pkg.dev/cloudrun/container/job",
                },
            ],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

default = gcp.cloudrunv2.Job("default",
    name="cloudrun-job",
    location="us-central1",
    deletion_protection=False,
    template={
        "template": {
            "containers": [
                {
                    "name": "job-1",
                    "image": "us-docker.pkg.dev/cloudrun/container/job",
                },
                {
                    "name": "job-2",
                    "image": "us-docker.pkg.dev/cloudrun/container/job",
                },
            ],
        },
    })
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cloudrunv2.NewJob(ctx, "default", &cloudrunv2.JobArgs{
			Name:               pulumi.String("cloudrun-job"),
			Location:           pulumi.String("us-central1"),
			DeletionProtection: pulumi.Bool(false),
			Template: &cloudrunv2.JobTemplateArgs{
				Template: &cloudrunv2.JobTemplateTemplateArgs{
					Containers: cloudrunv2.JobTemplateTemplateContainerArray{
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Name:  pulumi.String("job-1"),
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
						},
						&cloudrunv2.JobTemplateTemplateContainerArgs{
							Name:  pulumi.String("job-2"),
							Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/job"),
						},
					},
				},
			},
		})
		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.CloudRunV2.Job("default", new()
    {
        Name = "cloudrun-job",
        Location = "us-central1",
        DeletionProtection = false,
        Template = new Gcp.CloudRunV2.Inputs.JobTemplateArgs
        {
            Template = new Gcp.CloudRunV2.Inputs.JobTemplateTemplateArgs
            {
                Containers = new[]
                {
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Name = "job-1",
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                    },
                    new Gcp.CloudRunV2.Inputs.JobTemplateTemplateContainerArgs
                    {
                        Name = "job-2",
                        Image = "us-docker.pkg.dev/cloudrun/container/job",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.cloudrunv2.Job;
import com.pulumi.gcp.cloudrunv2.JobArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateArgs;
import com.pulumi.gcp.cloudrunv2.inputs.JobTemplateTemplateArgs;
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 Job("default", JobArgs.builder()
            .name("cloudrun-job")
            .location("us-central1")
            .deletionProtection(false)
            .template(JobTemplateArgs.builder()
                .template(JobTemplateTemplateArgs.builder()
                    .containers(                    
                        JobTemplateTemplateContainerArgs.builder()
                            .name("job-1")
                            .image("us-docker.pkg.dev/cloudrun/container/job")
                            .build(),
                        JobTemplateTemplateContainerArgs.builder()
                            .name("job-2")
                            .image("us-docker.pkg.dev/cloudrun/container/job")
                            .build())
                    .build())
                .build())
            .build());

    }
}
resources:
  default:
    type: gcp:cloudrunv2:Job
    properties:
      name: cloudrun-job
      location: us-central1
      deletionProtection: false
      template:
        template:
          containers:
            - name: job-1
              image: us-docker.pkg.dev/cloudrun/container/job
            - name: job-2
              image: us-docker.pkg.dev/cloudrun/container/job

The containers array defines multiple containers that execute simultaneously within the same job execution. Each container needs a unique name property. Containers share the same network namespace and can communicate via localhost, but run separate processes. This pattern works well for sidecar containers that handle logging, monitoring, or data transformation alongside the main application.

Beyond these examples

These snippets focus on specific job-level features: container deployment and resource limits, VPC connectivity (connectors and direct interfaces), and secrets and volume management. They’re intentionally minimal rather than full batch processing systems.

The examples may reference pre-existing infrastructure such as Cloud SQL instances, Secret Manager secrets with IAM bindings, and VPC networks, subnets, and VPC Access Connectors. They focus on configuring the job rather than provisioning everything around it.

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

  • Job execution triggers (startExecutionToken, runExecutionToken)
  • Binary Authorization policies (binaryAuthorization)
  • GPU node selection (nodeSelector, gpuZonalRedundancyDisabled)
  • Annotations and labels for metadata
  • Parallelism and retry configuration

These omissions are intentional: the goal is to illustrate how each job feature is wired, not provide drop-in batch processing modules. See the Cloud Run Job resource reference for all available configuration options.

Let's deploy GCP Cloud Run Jobs

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Deletion & Lifecycle
Why can't I delete my Cloud Run job?
Cloud Run jobs have deletionProtection enabled by default. You must explicitly set deletionProtection: false in your configuration before running pulumi up to delete the job.
What properties can't be changed after creating a job?
The location, name, and project properties are immutable. Changing any of these requires destroying and recreating the job.
Metadata & Labels
Why aren't my labels and annotations showing up in GCP Console?
The labels and annotations fields are non-authoritative, meaning they only manage values present in your Pulumi configuration. To see all labels and annotations (including those added by GCP or other tools), use the effectiveLabels and effectiveAnnotations output properties.
What label and annotation namespaces are forbidden?
You cannot use run.googleapis.com, cloud.googleapis.com, serving.knative.dev, or autoscaling.knative.dev namespaces in custom labels or annotations. These are reserved for system use and will be rejected.
Execution & Triggers
What's the difference between startExecutionToken and runExecutionToken?
Both trigger a new execution when creating or updating a job, but startExecutionToken marks the job ready when execution starts, while runExecutionToken waits until execution completes successfully. Both are Beta features.
What's the character limit for execution tokens?
The combined length of your job name and execution token (startExecutionToken or runExecutionToken) must be fewer than 63 characters.
How do I use preview features like GPU support?
Set launchStage to ALPHA or BETA to enable preview features. If not specified, launchStage defaults to GA (generally available features only).
Networking & VPC
How do I connect my Cloud Run job to VPC resources?

You have two options:

  1. Serverless VPC Access - Use vpcAccess.connector with a VPC Access connector ID
  2. Direct VPC egress - Use vpcAccess.networkInterfaces to specify network, subnetwork, and tags directly
How do I connect my job to Cloud SQL?
Create a volume with cloudSqlInstance.instances pointing to your database’s connection name, then mount it in your container using volumeMounts. You’ll also need to grant the service account roles/secretmanager.secretAccessor if using secrets for credentials.
Secrets & Configuration
How do I mount secrets from Secret Manager?
Define a volume with secret configuration referencing your Secret Manager secret ID, then mount it using volumeMounts in your container. Ensure the service account has roles/secretmanager.secretAccessor permission.
How do I pass environment variables to my job?
Use the envs array in your container configuration. You can set static values with name and value, or reference secrets with valueSource.secretKeyRef.
Resources & Performance
How do I set CPU and memory limits?
Configure resources.limits within your container specification. For example: cpu: "2" and memory: "1024Mi".
How do I configure GPU support for my job?
Set nodeSelector.accelerator to your desired GPU type (e.g., nvidia-l4) and optionally set gpuZonalRedundancyDisabled: true to disable zonal redundancy.
How do I create ephemeral storage for my job?
Define a volume with emptyDir configuration specifying medium (e.g., MEMORY) and sizeLimit (e.g., 128Mi), then mount it using volumeMounts.

Using a different cloud?

Explore serverless guides for other cloud providers: