# gcloud CLI Cheat Sheet — Serverless: Cloud Functions, Pub/Sub, Eventarc & Scheduler

> **Tool:** Google Cloud CLI (gcloud)
> **Category:** Cloud CLIs
> **Verified against:** Google Cloud SDK 553.0.0, flags verified via `gcloud functions deploy --help`,
> `gcloud pubsub topics create --help`, `gcloud pubsub subscriptions create --help`, `gcloud pubsub topics
> publish --help`, `gcloud pubsub subscriptions pull --help`, `gcloud eventarc triggers create --help`,
> `gcloud scheduler jobs create http --help`, `gcloud scheduler jobs create pubsub --help`, and `gcloud
> tasks queues create --help` run locally, 2026-09-17
> **Official docs:** https://cloud.google.com/sdk/gcloud/reference/functions, https://cloud.google.com/sdk/gcloud/reference/pubsub, and https://cloud.google.com/sdk/gcloud/reference/eventarc

## What it is and where it fits 🎯

Cloud Run (page `02`) is GCP's general-purpose serverless container platform; this page covers the layer of
services built specifically around *events*, rather than long-running HTTP services. Cloud Functions is a
function-as-a-service runtime (single-purpose, one trigger, one piece of code, the equivalent of AWS Lambda
or Azure Functions), Pub/Sub is GCP's managed message queue/event bus, Eventarc is the routing layer that
turns dozens of different GCP event sources into a uniform trigger any of these can react to, and Cloud
Scheduler/Cloud Tasks handle time-based and deferred-work triggering respectively. This page assumes the
project/auth basics from page `01`; Cloud Functions (2nd gen) is actually built on Cloud Run under the hood,
so several concepts from page `02`'s Cloud Run section (concurrency, `--min-instances`, revisions) apply
here too.

## Core concepts: how an event actually reaches your code

```mermaid
flowchart LR
    Source["Event source<br/>(GCS upload, Firestore write,<br/>Pub/Sub message, HTTP call)"] --> Eventarc["Eventarc<br/>(routes + filters)"]
    Eventarc --> Function["Cloud Function<br/>(gen2, runs on Cloud Run)"]
    Eventarc --> Run["Cloud Run service"]
    Direct["Pub/Sub message"] -.->|"push subscription,<br/>bypasses Eventarc"| Function
    Scheduler["Cloud Scheduler<br/>(cron)"] --> Direct

    classDef info fill:#e5f0fa,stroke:#1d6fb8,color:#10161c
    classDef accent fill:#f0e9fb,stroke:#6d43c0,color:#10161c
    classDef muted fill:#eaeef1,stroke:#c3ccd4,color:#10161c
    class Eventarc info
    class Function,Run accent
    class Source,Direct,Scheduler muted
```

Two real trigger paths exist for a 2nd-gen Cloud Function, and picking the right one matters. **Eventarc**
is the general-purpose router, subscribing to it gives a function access to dozens of event source types
(Cloud Storage object changes, Firestore document writes, Cloud Audit Logs entries, Pub/Sub messages, and
more) through one consistent CloudEvents-formatted payload. A **direct Pub/Sub trigger**
(`--trigger-topic`) is the older, simpler, and still entirely valid shortcut specifically for "run this
function whenever a message lands on this topic", skipping Eventarc's extra hop when you don't need its
broader source catalog.

## Deploying a Cloud Function

```bash
gcloud functions deploy process-upload \
  --gen2 --runtime=python312 --region=us-central1 \
  --source=. --entry-point=handle_upload \
  --trigger-bucket=my-uploads-bucket \
  --memory=512Mi --timeout=120s --max-instances=20 \
  --service-account=processor@my-project-id.iam.gserviceaccount.com
```

- **`--gen2`** targets 2nd-generation Cloud Functions, built on Cloud Run and Eventarc, with a larger
  request timeout ceiling and concurrency support gen1 never had; new functions should default to it.
- **`--entry-point`** names the specific function inside your source file that's the actual handler, the
  rest of the file can hold helpers, imports, whatever the runtime needs.
- **`--trigger-bucket`** is shorthand for an Eventarc trigger on that bucket's object-finalize events;
  `--trigger-topic`, `--trigger-http`, and `--trigger-event-filters` (the general Eventarc form) are the
  other trigger shapes, mutually exclusive with each other.
- **`--max-instances`** caps concurrent scale-out the same way it does on Cloud Run, worth setting
  deliberately rather than leaving unbounded on anything triggered by an event source you don't fully
  control the volume of (a public upload bucket, say).

## HTTP-triggered functions and authentication

```bash
gcloud functions deploy api-webhook \
  --gen2 --runtime=nodejs20 --region=us-central1 \
  --source=. --entry-point=handleWebhook \
  --trigger-http --no-allow-unauthenticated

gcloud functions add-invoker-policy-binding api-webhook \
  --region=us-central1 --member="serviceAccount:caller@my-project-id.iam.gserviceaccount.com"
```

`--trigger-http` gives the function its own HTTPS endpoint, functionally identical to a Cloud Run service at
that point, `--no-allow-unauthenticated` (the safer default, same as Cloud Run on page `02`) requires every
caller to present a valid IAM-authenticated identity token. `add-invoker-policy-binding` is the function-
specific equivalent of granting `roles/cloudfunctions.invoker` (or, on the underlying Cloud Run service,
`roles/run.invoker`) to a specific caller identity, without which an authenticated-but-unauthorized caller
gets a `403`.

## Managing deployed functions

```bash
gcloud functions list --regions=us-central1
gcloud functions describe process-upload --region=us-central1
gcloud functions describe process-upload --region=us-central1 --format="value(state)"

gcloud functions logs read process-upload --region=us-central1 --limit=50
gcloud functions delete process-upload --region=us-central1
```

`gcloud functions deploy` against an existing function name updates it in place, deploying a new revision
rather than creating a duplicate, the same "just redeploy" model as `gcloud run deploy`; there's no separate
"update" subcommand for the function's code or trigger configuration.

## Pub/Sub: topics and publishing

```bash
gcloud pubsub topics create order-events
gcloud pubsub topics list
gcloud pubsub topics publish order-events --message='{"orderId": "12345", "status": "shipped"}' \
  --attribute=eventType=order.shipped
```

> [!NOTE]
> `gcloud pubsub topics publish` is documented as being for testing and troubleshooting, not production
> traffic, an application should publish through a client library, which batches and retries properly. All
> subscribers to a topic must be able to consume and acknowledge whatever gets published, including a
> message sent manually this way, or Pub/Sub keeps re-attempting delivery of it for up to 7 days.

## Pub/Sub: subscriptions

```bash
gcloud pubsub subscriptions create order-events-processor \
  --topic=order-events --ack-deadline=60 \
  --dead-letter-topic=order-events-dlq --max-delivery-attempts=5

gcloud pubsub subscriptions create order-events-webhook \
  --topic=order-events --push-endpoint=https://my-service.a.run.app/pubsub-push \
  --push-auth-service-account=pubsub-invoker@my-project-id.iam.gserviceaccount.com

gcloud pubsub subscriptions pull order-events-processor --auto-ack --limit=5
```

A **pull subscription** (the first example) is what a worker actively polls, appropriate for a service that
controls its own consumption rate; a **push subscription** (the second) has Pub/Sub actively HTTP-POST the
message to an endpoint, appropriate for a Cloud Run/Functions consumer that doesn't want to run a
long-lived polling loop at all. `--dead-letter-topic` combined with `--max-delivery-attempts` is the
pattern that keeps one permanently-failing message from being retried forever, after the attempt count is
exhausted it's routed to the dead-letter topic instead, where it can be inspected without blocking the rest
of the subscription's throughput.

> [!IMPORTANT]
> **A dead-letter topic needs its own IAM grant before Pub/Sub can actually publish to it** — the Pub/Sub
> service agent needs `roles/pubsub.publisher` on the dead-letter topic specifically. Skipping this grant is
> a common "messages just vanish after the retry limit instead of showing up in the DLQ" surprise, since the
> subscription creation itself succeeds even with the grant missing; the failure only surfaces the first
> time a message actually exhausts its retries.

## Eventarc: routing GCP events to a destination

```bash
gcloud eventarc triggers create gcs-upload-trigger \
  --location=us-central1 \
  --event-filters="type=google.cloud.storage.object.v1.finalized" \
  --event-filters="bucket=my-uploads-bucket" \
  --destination-run-service=process-upload-run \
  --destination-run-region=us-central1 \
  --service-account=eventarc-invoker@my-project-id.iam.gserviceaccount.com

gcloud eventarc triggers list --location=us-central1
gcloud eventarc triggers describe gcs-upload-trigger --location=us-central1
```

Every `--event-filters` entry narrows which events actually invoke the destination, `type` is mandatory and
identifies the CloudEvents event type; additional filters (like `bucket` above) further scope it to a
specific resource rather than every bucket in the project. Eventarc's destination can be a Cloud Run
service, a GKE service, a Cloud Function, or a workflow, not just a function, making it the shared routing
layer across every compute option this site's GCP pages cover, not something Cloud Functions-specific.

## Cloud Scheduler: cron-triggered jobs

```bash
gcloud scheduler jobs create http nightly-report \
  --location=us-central1 --schedule="0 2 * * *" \
  --uri=https://my-service.a.run.app/generate-report \
  --http-method=POST \
  --oidc-service-account-email=scheduler-invoker@my-project-id.iam.gserviceaccount.com

gcloud scheduler jobs create pubsub cleanup-trigger \
  --location=us-central1 --schedule="*/15 * * * *" \
  --topic=cleanup-events --message-body='{"action":"purge-expired"}'

gcloud scheduler jobs list --location=us-central1
gcloud scheduler jobs run nightly-report --location=us-central1   # trigger it immediately, outside the schedule
```

`--schedule` takes standard cron syntax, evaluated in `--time-zone` (UTC by default). `create http` invokes
a URL directly, `--oidc-service-account-email` is what authenticates that call as a real IAM identity rather
than an anonymous HTTP request, the Scheduler equivalent of the WIF-authenticated CI patterns on page `01`.
`create pubsub` publishes onto a topic on schedule instead, useful when several downstream consumers should
all react to the same scheduled tick via their own subscriptions rather than one job calling one URL
directly. `jobs run` is genuinely useful for testing a job's actual behavior without waiting for its next
scheduled tick.

## Cloud Tasks: deferred and rate-limited work

```bash
gcloud tasks queues create email-queue \
  --location=us-central1 \
  --max-dispatches-per-second=10 --max-concurrent-dispatches=50 \
  --max-attempts=5

gcloud tasks queues describe email-queue --location=us-central1
gcloud tasks queues pause email-queue --location=us-central1     # stop dispatching without deleting the queue
```

Cloud Tasks solves a different problem than Pub/Sub: explicit per-task control (schedule a specific task for
30 minutes from now, retry with a specific backoff, guarantee at-most-once execution ordering per queue) at
the cost of needing a known HTTP endpoint to dispatch to, rather than Pub/Sub's fan-out-to-many-subscribers
model. `--max-dispatches-per-second` is the mechanism for rate-limiting calls to a downstream system that
can't handle unbounded concurrency, a third-party email API with its own rate limits, for instance, in a way
a raw Pub/Sub push subscription has no equivalent knob for.

## Real-world scenario: image-processing pipeline with a dead-letter safety net

A photo-sharing app needs every uploaded image resized into three formats, with failures visible instead of
silently retried forever:

```bash
gcloud pubsub topics create image-resize-dlq

gcloud eventarc triggers create image-upload-trigger \
  --location=us-central1 \
  --event-filters="type=google.cloud.storage.object.v1.finalized" \
  --event-filters="bucket=user-uploads" \
  --destination-run-service=resize-worker --destination-run-region=us-central1 \
  --service-account=eventarc-invoker@my-project-id.iam.gserviceaccount.com

gcloud pubsub topics add-iam-policy-binding image-resize-dlq \
  --member="serviceAccount:service-<project-number>@gcp-sa-pubsub.iam.gserviceaccount.com" \
  --role="roles/pubsub.publisher"
```

The dead-letter IAM grant is applied explicitly rather than assumed, per the IMPORTANT callout above, before
the pipeline ever ships a genuinely malformed image that would otherwise vanish silently after exhausting
its retries.

## Real-world scenario: nightly batch job pre-flight checklist

- [ ] Confirm the target Cloud Run service or function has `--no-allow-unauthenticated` and Scheduler's
      service account is explicitly granted invoker on it, not relying on a broad IAM binding
- [ ] Set `--attempt-deadline` on the job to something shorter than the target's own request timeout, so a
      genuinely hung downstream call fails the Scheduler job cleanly rather than the job itself timing out
      first with a less useful error
- [ ] Run `gcloud scheduler jobs run <job-name>` manually once and inspect the actual response/logs before
      trusting the cron schedule to validate it for you overnight
- [ ] Confirm `--time-zone` matches what the team actually expects, `0 2 * * *` in UTC is a very different
      wall-clock hour for a team based outside UTC

## CI/CD integration recipe: deploying an event-triggered function on push

```yaml
# .github/workflows/deploy-function.yml
name: Deploy function
on:
  push:
    branches: [main]
    paths: ["functions/process-upload/**"]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: projects/<project-number>/locations/global/workloadIdentityPools/github-pool/providers/github-provider
          service_account: ci@my-project-id.iam.gserviceaccount.com
      - uses: google-github-actions/setup-gcloud@v2
      - run: |
          gcloud functions deploy process-upload \
            --gen2 --runtime=python312 --region=us-central1 \
            --source=functions/process-upload --entry-point=handle_upload \
            --trigger-bucket=my-uploads-bucket
```

The `paths` filter on the workflow trigger keeps this deploy from firing on unrelated commits elsewhere in
a monorepo, worth pairing with any function deploy pipeline living alongside other services in one repo.

## Common pitfalls

- **Publishing directly via `gcloud pubsub topics publish` from production code paths.** It's a
  troubleshooting tool; use a client library for real traffic, per the NOTE above.
- **Creating a dead-letter subscription without granting the Pub/Sub service agent publish rights on it.**
  Messages exhausting their retries vanish instead of landing in the DLQ, see the IMPORTANT callout above.
- **Leaving a Scheduler job's `--attempt-deadline` unset on a call to a slow or occasionally-hanging
  endpoint.** The job then waits on the target's own (often longer) timeout instead of failing fast.
- **Reaching for Cloud Tasks when Pub/Sub's simpler fan-out model would do, or vice versa.** Tasks are for
  explicit per-task scheduling/rate-limiting against one known endpoint; Pub/Sub is for fan-out to
  potentially many independent subscribers.
- **Forgetting `--gen2` and getting 1st-generation defaults.** Gen1 functions have a lower timeout ceiling
  and no Eventarc-based trigger catalog, worth confirming explicitly rather than assuming the current
  default matches what a tutorial or older doc page describes.

## Exit codes

`0` success, non-zero on any API/validation error, `gcloud functions deploy` in particular can fail at build
time (a broken dependency, a syntax error in the source) with a non-zero exit and a build-log URL printed to
stderr, `gcloud functions logs read <name> --min-log-level=error` is the fastest way to see the actual
runtime (as opposed to build-time) failure once a function is deployed but misbehaving.

## When to reach for something else

For a stateless HTTP service with no genuine single-purpose-function shape, or one that needs more control
over concurrency, startup behavior, or a non-standard runtime, prefer Cloud Run directly (page `02`) over
Cloud Functions, gen2 functions are Cloud Run underneath anyway, and Cloud Run's own `gcloud run deploy`
exposes strictly more configuration surface. For declarative, reviewable event-routing and queue
provisioning across environments, prefer Terraform's `google_cloudfunctions2_function`/
`google_pubsub_topic`/`google_eventarc_trigger`/`google_cloud_scheduler_job` resources over a growing shell
script of the commands on this page, consistent with the IaC guidance on earlier pages.
