Logo
Trigger Cloud Functions only for specific GCS folders

Trigger Cloud Functions only for specific GCS folders

May 25, 2026
6 min read

The problem with default storage triggers

A typical file processing workflow on Google Cloud Storage looks like this:

  1. A user uploads a file to gs://bucket/videos/video.mp4.
  2. A Cloud Function or Cloud Run service processes the file and generates a preview image.
  3. The function saves the image back to gs://bucket/thumbnails/video_thumbnail.png.

Wiring this workflow to a standard google.cloud.storage.object.v1.finalized trigger creates an instant loop.


Why default GCS triggers loop

The default finalized trigger fires on every object creation in the bucket:

type: google.cloud.storage.object.v1.finalized
bucket: my-media-bucket

When videos/video.mp4 lands in the bucket, your function runs and writes thumbnails/video_thumbnail.png. Because that thumbnail is also a new object in the same bucket, Cloud Storage fires another finalized event. The function runs again, generates another file, and loops until memory limits, execution timeouts, or concurrency caps stop it.

A single upload can trigger hundreds of billable function invocations in minutes.

Warning - Infinite loop risk

Unfiltered recursive triggers can burn through Cloud Functions quotas and rack up billing charges within hours.


Why code-level checks fall short

The standard workaround checks the object path in code and exits early:

if (objectName.startsWith('thumbnails/')) {
return;
}

Or filters by extension:

if (!objectName.endsWith('.mp4')) {
return;
}

Early returns stop your heavy processing logic, but the function still boots up. You still pay for the invocation, cold starts consume resources, and logs fill with junk events.

You want the platform to drop or filter the event before your compute runs. You have two practical ways to do that: Eventarc path patterns through Cloud Audit Logs, or Cloud Storage bucket notifications via Pub/Sub.


Option 1: Filter paths with Cloud Audit Logs and Eventarc

Eventarc lets you filter on path patterns if you route events through Cloud Audit Logs.

Instead of listening to google.cloud.storage.object.v1.finalized, you create an Eventarc trigger for google.cloud.audit.log.v1.written events. You set a resource path pattern targeting objects/videos/*. When your function writes output to thumbnails/, Eventarc ignores the event and never invokes your code.

Architecture

graph TD
    User(["User<br/>uploads video"])

    GCS_Videos[("GCS Bucket<br/>/videos")]

    AuditLogs["Cloud Audit Logs<br/>storage.objects.create"]

    Eventarc{"Eventarc Trigger<br/>path filter: /videos/**"}

    Compute["Cloud Run / Cloud Function<br/>generates thumbnail"]

    Thumb(["Thumbnail<br/>ready"])

    GCS_Thumbs[("GCS Bucket<br/>/thumbnails")]

    User -->|"1. HTTPS upload"| GCS_Videos
    GCS_Videos -.->|"2. Emits audit log event"| AuditLogs
    AuditLogs -->|"3. Forwards event"| Eventarc
    Eventarc -->|"4. Matches /videos/** only, /thumbnails ignored"| Compute
    Compute -->|"5. Processes frame"| Thumb
    Thumb -->|"6. Stores output"| GCS_Thumbs

    style GCS_Videos  fill:#1A73E8,stroke:#0D47A1,color:#fff
    style GCS_Thumbs  fill:#1A73E8,stroke:#0D47A1,color:#fff
    style AuditLogs   fill:#EA4335,stroke:#B31412,color:#fff
    style Eventarc    fill:#F9AB00,stroke:#E37400,color:#1a1a1a
    style Compute     fill:#34A853,stroke:#1E7E34,color:#fff
    style Thumb       fill:#34A853,stroke:#1E7E34,color:#fff
    style User        fill:#E8F0FE,stroke:#4285F4,color:#1a1a1a

Set up Eventarc path filtering

  1. Enable Data Write audit logs for Cloud Storage. Go to IAM & Admin > Audit Logs in Google Cloud Console. Select Google Cloud Storage, check Data Write, and save.

  2. Create the Eventarc trigger using gcloud.

Terminal window
gcloud eventarc triggers create videos-folder-trigger \
--location=${REGION} \
--destination-run-service=thumbnail-service \
--destination-run-region=${REGION} \
--event-filters="type=google.cloud.audit.log.v1.written" \
--event-filters="serviceName=storage.googleapis.com" \
--event-filters="methodName=storage.objects.create" \
--event-filters-path-pattern="resourceName=projects/_/buckets/bucket-name/objects/videos/*" \
--service-account=YOUR_EVENTARC_SERVICE_ACCOUNT
  1. Handle the event in your function.
const functions = require('@google-cloud/functions-framework');
functions.cloudEvent('thumbnailTrigger', (event) => {
const payload = event.data?.protoPayload || {};
console.log('Event ID:', event.id);
console.log('Method Name:', payload.methodName);
console.log('Resource Name:', payload.resourceName);
});

Option 2: Filter folder prefixes with GCS bucket notifications

Cloud Storage includes a built-in notification system that publishes directly to Pub/Sub topics. Unlike default Eventarc bucket triggers, bucket notifications support prefix filtering natively with --object-prefix.

When files land under trigger/ or videos/, Cloud Storage publishes an event to your topic. Writes to thumbnails/ or other directories never hit Pub/Sub, so downstream consumers never wake up.

1. Create the bucket notification

Run the gcloud storage command to attach a notification configuration to your bucket:

Terminal window
gcloud storage buckets notifications create gs://bucket-name \
--topic=folder_trigger \
--object-prefix="trigger/" \
--event-types="OBJECT_FINALIZE"

If Cloud Storage lacks publish permissions on your topic, grant the publisher role to the Cloud Storage service account:

Terminal window
GCS_SERVICE_ACCOUNT=$(gcloud storage service-agent)
gcloud pubsub topics add-iam-policy-binding folder_trigger \
--member="serviceAccount:${GCS_SERVICE_ACCOUNT}" \
--role="roles/pubsub.publisher"

2. Consume events in Firebase Functions

If you use Firebase Functions v2, subscribe to the topic with onMessagePublished:

import { logger } from "firebase-functions/logger";
import { onMessagePublished } from "firebase-functions/v2/pubsub";
export const folder_trigger = onMessagePublished({
topic: "folder_trigger",
region: "us-central1",
},
async (event) => {
const messageData = event.data.message.json;
const bucket = messageData.bucket;
const filePath = messageData.name;
const size = messageData.size;
logger.info(`Processing file upload: ${bucket}/${filePath} (${size} bytes)`);
return null;
}
);

Firebase parses the incoming Pub/Sub JSON payload in event.data.message.json, giving you direct access to bucket, name, and size.

3. Consume events in Cloud Functions (2nd gen) or Cloud Run

For standard Cloud Functions (2nd gen) with the Functions Framework, decode the message payload from base64:

const functions = require('@google-cloud/functions-framework');
functions.cloudEvent('folderTrigger', (cloudEvent) => {
const base64Data = cloudEvent.data.message.data;
const messageData = JSON.parse(Buffer.from(base64Data, 'base64').toString());
const bucket = messageData.bucket;
const filePath = messageData.name;
const size = messageData.size;
console.log(`Processing file upload: ${bucket}/${filePath} (${size} bytes)`);
});

Deploy the function with a Pub/Sub trigger:

Terminal window
gcloud functions deploy folder-trigger \
--gen2 \
--runtime=nodejs20 \
--region=us-central1 \
--source=. \
--entry-point=folderTrigger \
--trigger-topic=folder_trigger

You can also point an Eventarc Pub/Sub trigger to the folder_trigger topic if your service runs on Cloud Run.


Compare the approaches

FeatureDefault GCS finalizedEventarc + Audit LogsGCS Pub/Sub notifications
Bucket filteringYesYesYes
Folder filteringNoYes (wildcards and globs)Yes (prefix string)
Early return boilerplateRequiredNot neededNot needed
Redundant invocationsManyNoneNone
Recursive loop riskHighNoneNone
Setup complexityLowMedium (needs Audit Logs)Low (one gcloud command)
Good fitSingle-folder bucketsComplex glob patternsPub/Sub pipelines and Firebase

Cost and volume considerations

  • GCS Data Write audit logs write to Cloud Logging. Google Cloud gives you 50 GiB of free logging per project each month. At 10,000 uploads a month, the audit trail generates under 30 MiB of logs.
  • Pub/Sub offers 10 GiB of free message ingestion and delivery per month. For moderate workloads, GCS bucket notifications stay within the free tier.

Traps to avoid

  • Directory placeholder objects. Creating folders in the Cloud Storage console writes zero-byte objects ending with a trailing slash (trigger/ or videos/). Add if (filePath.endsWith('/')) return; at the start of your handler to skip them.
  • Payload format differences. Audit Log events store resource names in event.data.protoPayload.resourceName. Pub/Sub bucket notifications pass object metadata directly (bucket, name, size) in the JSON message body. Keep the handler schema aligned with the method you choose.
  • Idempotency. Eventarc and Pub/Sub both deliver messages at least once. If network hiccups or function timeouts happen, your handler can receive duplicate events. Make your processing step idempotent so re-processing the same file produces identical results without side effects.