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 FREEFrequently Asked Questions
Configuration & Immutability
location, name, and project properties are immutable and cannot be changed after job creation.startExecutionToken or runExecutionToken) must be fewer than 63 characters.Labels & Annotations
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.run.googleapis.com, cloud.googleapis.com, serving.knative.dev, or autoscaling.knative.dev namespaces.Networking & VPC
You have two options:
- Connector-based - Use
vpcAccess.connectorwith a VPC Access Connector ID andegresssetting - Direct VPC - Use
vpcAccess.networkInterfacesto specify network, subnetwork, and tags directly
cloudSqlInstance volume with the instance connection name, then mount it in your container using volumeMounts with the volume name and mount path.Volumes & Storage
Cloud Run jobs support three volume types:
- cloudSqlInstance - For Cloud SQL database connections
- secret - For mounting Secret Manager secrets as files
- emptyDir - For temporary storage (can use memory or disk)
You can use secrets in two ways:
- Environment variables - Use
envs[].valueSource.secretKeyRefto reference a secret version - Volume mounts - Use
volumes[].secretto mount secrets as files in your container
Execution & Resources
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.template.template.containers[].resources.limits with cpu (e.g., "2") and memory (e.g., "1024Mi") values.template.template.containers array. Each container must have a unique name property.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: