Deploy GCP Cloud Run Jobs

The gcp:cloudrunv2/job:Job resource, part of the Pulumi GCP provider, defines a Cloud Run Job that references a container image and runs it to completion on demand or on schedule. This guide focuses on four capabilities: container deployment with resource limits, VPC connectivity options, secrets and volume mounting, and multi-container tasks.

Cloud Run Jobs reference container images from Artifact Registry, VPC infrastructure for private connectivity, and Secret Manager for credentials. The examples are intentionally small. Combine them with your own container images, networking, and secrets management.

Deploy a job with a container image

Most deployments start by specifying a container image and location. The job defines the execution template but doesn’t run 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 won’t execute until you trigger it manually, via Cloud Scheduler, or using startExecutionToken.

Set CPU and memory resource limits

Jobs processing large datasets 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 compute allocation. CPU is specified in cores (e.g., “2” for 2 vCPUs), and memory uses units like “1024Mi” for 1 GiB. These limits apply to each task instance.

Connect to Cloud SQL with secrets and volumes

Jobs that query databases need Cloud SQL connectivity through Unix sockets and credentials from 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 (
	"fmt"

	"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: {}

The volumes array defines a cloudSqlInstance volume that mounts the Cloud SQL Unix socket. The envs array injects secrets via valueSource.secretKeyRef, referencing Secret Manager. The volumeMounts property makes the socket available at /cloudsql inside the container. Your service account needs the secretAccessor role.

Route traffic through VPC Access Connector

Jobs reaching internal services use Serverless VPC Access connectors for private connectivity.

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 your VPC.

Connect directly to VPC with network interfaces

Direct VPC egress attaches network interfaces to job tasks without requiring a connector.

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 specifies the VPC network and subnetwork directly. The tags property applies network tags for firewall rule matching. This approach avoids connector overhead for simpler VPC connectivity.

Mount secrets as files in the container

Applications reading configuration from files can mount Secret Manager secrets 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 (
	"fmt"

	"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 array defines a secret volume with items that map secret versions to file paths. The defaultMode sets Unix permissions (292 is 0444 in octal). The volumeMounts property makes secrets available at /secrets inside the container.

Use in-memory storage for temporary data

Jobs needing fast temporary storage can use emptyDir volumes backed by memory.

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 emptyDir volume provides ephemeral storage that persists across container restarts within a task. The medium property set to “MEMORY” uses RAM instead of disk. The sizeLimit property caps storage at 128 MiB.

Run multiple containers in a single task

Complex jobs can run multiple containers that share network namespace and 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: [
                {
                    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 accepts multiple container definitions, each with a name property. Containers in the same task share localhost networking and can mount the same volumes, enabling sidecar patterns like logging or monitoring agents.

Beyond these examples

These snippets focus on specific job-level features: container deployment and resource limits, VPC connectivity (connectors and direct interfaces), secrets and volumes (Cloud SQL, Secret Manager, emptyDir), and multi-container tasks. They’re intentionally minimal rather than full batch processing systems.

The examples may reference pre-existing infrastructure such as container images in Artifact Registry or Container Registry, VPC networks, subnets, and VPC Access Connectors, Cloud SQL instances and Secret Manager secrets, and IAM permissions for service accounts. They focus on configuring the job rather than provisioning everything around it.

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

  • Execution triggers (startExecutionToken, runExecutionToken)
  • Binary Authorization policies (binaryAuthorization)
  • GPU configuration (nodeSelector with accelerator)
  • Annotations and labels for metadata
  • Task parallelism and retry configuration
  • Service account assignment

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

Configuration & Immutability
What properties can't I change after creating a job?
The location, name, and project properties are immutable and cannot be changed after job creation.
What's the character limit when using execution tokens?
The sum of the job name and execution token (startExecutionToken or runExecutionToken) must be fewer than 63 characters.
Labels & Annotations
Why don't I see all my labels and annotations in my configuration?
The labels and annotations fields are non-authoritative, meaning they only manage values present in your configuration. To see all labels and annotations on the resource (including those set by other clients or services), use the effectiveLabels and effectiveAnnotations output properties.
What label and annotation namespaces are forbidden?
Cloud Run API v2 rejects annotations and labels with run.googleapis.com, cloud.googleapis.com, serving.knative.dev, or autoscaling.knative.dev namespaces.
Networking & VPC
What are my options for connecting a job to a VPC?

You have two options:

  1. Connector-based - Use vpcAccess.connector with a VPC Access Connector ID and egress setting
  2. Direct VPC - Use vpcAccess.networkInterfaces to specify network, subnetwork, and tags directly
How do I connect my job to Cloud SQL?
Create a cloudSqlInstance volume with the instance connection name, then mount it in your container using volumeMounts with the volume name and mount path.
Volumes & Storage
What volume types can I use with Cloud Run jobs?

Cloud Run jobs support three volume types:

  1. cloudSqlInstance - For Cloud SQL database connections
  2. secret - For mounting Secret Manager secrets as files
  3. emptyDir - For temporary storage (can use memory or disk)
How do I use secrets in my job?

You can use secrets in two ways:

  1. Environment variables - Use envs[].valueSource.secretKeyRef to reference a secret version
  2. Volume mounts - Use volumes[].secret to mount secrets as files in your container
Execution & Resources
What's the difference between startExecutionToken and runExecutionToken?
startExecutionToken makes the job ready when execution successfully starts, while runExecutionToken makes the job ready when execution successfully completes. Both create a new execution when the job is created or updated.
How do I set CPU and memory limits?
Configure template.template.containers[].resources.limits with cpu (e.g., "2") and memory (e.g., "1024Mi") values.
Can I run multiple containers in a single job?
Yes, specify multiple containers in the template.template.containers array. Each container must have a unique name property.
How do I configure GPU support for my job?
Set template.template.nodeSelector.accelerator to your desired GPU type (e.g., "nvidia-l4"). You can also set gpuZonalRedundancyDisabled to control zonal redundancy.

Using a different cloud?

Explore serverless guides for other cloud providers: