Cloud Scheduler in GCP: What It Is, How It Works, and When to Use It

Cloud Scheduler is Google Cloud’s managed cron service. You define a schedule and a target — an HTTP endpoint, a Pub/Sub topic, or a Cloud Run Job — and Google fires it for you on time, every time. No VM to keep running, no cron daemon to restart after a reboot, no missed jobs during maintenance windows. For most cloud-native applications, it replaces the old pattern of running a cron job on a dedicated VM.

Cloud Scheduler in plain English

Cloud Scheduler is Google’s managed alarm clock for your cloud infrastructure. You set a time, tell it what to call, and it handles everything else — no servers required.

Analogy

Imagine setting a recurring calendar invite that, instead of notifying a person, automatically calls a phone number and reads out a message. Cloud Scheduler is exactly that: a managed timer that dials your service on a schedule. The difference from a cron job on a VM is that the “phone exchange” is Google’s — not a server you have to keep alive.

Here is the mental model in four steps:

  1. You write a cron expression, such as 0 9 * * * (every day at 09:00)
  2. You pick a target: an HTTP URL, a Pub/Sub topic, or a Cloud Run Job
  3. Google fires the trigger on the schedule, with retries if the target fails
  4. Every execution is logged automatically — check the result any time

If you have ever maintained a cron job on a Linux VM just to hit an API endpoint every night, Cloud Scheduler removes that entire layer of operational overhead.

How Cloud Scheduler works

Each Cloud Scheduler job has four components. Understanding these makes the gcloud commands much easier to read.

  • Schedule: a cron expression that determines when the job fires. Cloud Scheduler uses standard Unix cron syntax.

  • Target: where the trigger is sent. Either an HTTP/HTTPS endpoint, a Pub/Sub topic, or the Cloud Run Jobs API.

  • Authentication: either none (public endpoint) or an OIDC token generated from a service account identity, attached to the Authorization header automatically.

  • Retry policy: how many times to retry on a non-2xx response, with configurable backoff between attempts.

When the schedule fires, Cloud Scheduler sends an HTTP request to the target. For Pub/Sub targets, it publishes a message instead. A 2xx response records the execution as successful. A non-2xx response triggers the retry policy. Execution history is written to Cloud Logging automatically.

Tip

You can inspect the last execution result with gcloud scheduler jobs describe JOB_NAME —location=REGION. This shows the last attempt time, the HTTP status returned, and the error message if it failed. Check this first when a job is not behaving as expected.

What Cloud Scheduler can trigger

HTTP and HTTPS endpoints

The most common target. Cloud Scheduler sends an HTTP request with your chosen method (GET, POST, etc.), headers, and optional body. The endpoint can be any publicly reachable URL: a Cloud Run service, a Cloud Functions endpoint, an App Engine handler, or an external HTTPS URL.

Private Cloud Run and Cloud Functions (authenticated)

For services that should not be publicly callable, Cloud Scheduler attaches an OIDC token to the request. The token proves Cloud Scheduler’s identity. The target validates the token and rejects requests without one. This is the recommended pattern for scheduled batch endpoints — see the authenticated targets section below.

Pub/Sub topics

Cloud Scheduler publishes a message to a Pub/Sub topic on the schedule. One or more subscribers receive it and act on it. This pattern decouples the trigger from the processing and is useful when multiple systems need to react to the same scheduled event.

Cloud Run Jobs (via the API)

Cloud Scheduler triggers a Cloud Run Job by calling the Cloud Run Jobs API endpoint. This is the standard pattern for scheduled batch work: the job container starts, runs to completion, and exits. See the Cloud Run Jobs section below.

When to use Cloud Scheduler

Cloud Scheduler is the right tool for any workload that runs on a predictable, recurring schedule:

  • Generating daily or weekly reports and emailing them to stakeholders
  • Running nightly data exports from a database to Cloud Storage
  • Polling an external API every few minutes and storing the results
  • Triggering cleanup jobs: deleting old records, expiring sessions, archiving logs
  • Starting a Cloud Run Job for overnight batch processing
  • Publishing a Pub/Sub message on a schedule to kick off an event-driven workflow
  • Warming caches or pre-computing expensive queries before business hours
  • Sending scheduled notifications or digest emails

The key qualifier: you know the execution time in advance, and the job runs repeatedly on that same schedule.

When not to use Cloud Scheduler

Cloud Scheduler is not a general-purpose task queue. It fires one trigger per interval and has no concept of queued items. Avoid it when:

  • You need to process many individual items at a controlled rate. If user actions create background tasks that need async processing, use Cloud Tasks. It rate-limits dispatch, deduplicates tasks by name, and lets you schedule each task independently.

  • You need queue semantics. Cloud Scheduler does not queue up work items or let tasks wait behind each other.

  • You need per-execution deduplication. Cloud Scheduler has no named tasks per execution. Cloud Tasks lets you name individual tasks to prevent double-processing within a 4-hour window.

  • The trigger comes from user activity, not a clock. If a user signing up should trigger a welcome email, that is an event. Use Pub/Sub or Cloud Tasks instead.

How to create a Cloud Scheduler job

# Enable the API first
gcloud services enable cloudscheduler.googleapis.com

HTTP job: trigger an endpoint on a schedule

# Trigger a Cloud Run service every day at 09:00 London time
gcloud scheduler jobs create http daily-report \
  --schedule="0 9 * * *" \
  --uri="https://my-service-xyz.run.app/generate-report" \
  --http-method=POST \
  --message-body='{"report":"daily"}' \
  --headers="Content-Type=application/json" \
  --time-zone="Europe/London" \
  --location=us-central1

Pub/Sub job: publish a message on a schedule

# Publish a Pub/Sub message every 5 minutes
gcloud scheduler jobs create pubsub poll-every-5 \
  --schedule="*/5 * * * *" \
  --topic=my-topic \
  --message-body='{"action":"poll"}' \
  --location=us-central1
Watch out

Cloud Scheduler defaults to UTC. A job set to 0 9 * * * without —time-zone runs at 09:00 UTC. If your users expect 09:00 London time, that differs by one hour during British Summer Time. Always set —time-zone explicitly to avoid off-by-one-hour surprises during daylight saving transitions.

Cron syntax reference

Cloud Scheduler uses standard Unix cron syntax with five fields:

minute   hour   day-of-month   month   day-of-week

  • 0 9 * * *  — every day at 09:00
  • 0 9 * * 1  — every Monday at 09:00
  • */15 * * * * — every 15 minutes
  • 0 0 1 * *  — first day of every month at midnight
  • 0 8-18 * * 1-5 — every hour from 08:00 to 18:00, Monday to Friday
  • 30 6 * * 1,3,5 — 06:30 on Monday, Wednesday, and Friday

Cloud Scheduler also accepts @hourly, @daily, @weekly, and @monthly as aliases, though explicit cron expressions are clearer for anyone reading the job configuration later.

Common mistake

0 9 * * 0-5 is not “weekdays only”. Day 0 is Sunday, so this expression includes Sunday. Use 0 9 * * 1-5 for Monday to Friday. Test your expressions before relying on them in production.

Authenticated targets

Analogy

Think of OIDC authentication like a staff badge. Your private Cloud Run service is a secure building that only lets in people wearing a valid badge. Cloud Scheduler is the visitor — it picks up a temporary badge (the OIDC token) signed by your company (the service account), shows it at the door, and gets let in. Anyone without a badge is turned away at HTTP 403.

For private Cloud Run services and Cloud Functions, Cloud Scheduler generates a short-lived OIDC token signed with a service account identity and attaches it to the Authorization header. The target service validates the token and rejects requests without one. Read more about identity and service accounts if you are new to service-to-service authentication in GCP.

# Create a dedicated service account for Cloud Scheduler
gcloud iam service-accounts create scheduler-sa \
  --display-name="Cloud Scheduler invoker"

# Grant it permission to invoke the private Cloud Run service
gcloud run services add-iam-policy-binding my-private-service \
  --region=us-central1 \
  --member=serviceAccount:scheduler-sa@PROJECT_ID.iam.gserviceaccount.com \
  --role=roles/run.invoker

# Create the job with OIDC authentication
gcloud scheduler jobs create http call-private-service \
  --schedule="0 * * * *" \
  --uri="https://my-private-service-xyz.run.app/process" \
  --http-method=POST \
  --oidc-service-account-email=scheduler-sa@PROJECT_ID.iam.gserviceaccount.com \
  --time-zone="UTC" \
  --location=us-central1

The IAM role roles/run.invoker grants permission to call a Cloud Run service endpoint. Without it, requests return HTTP 403 even with a valid OIDC token.

Managing and testing jobs

Always run a job manually immediately after creating it. A wrong cron expression or misconfigured target URL will not surface until the next scheduled trigger. Manual execution catches these errors straight away.

# List all jobs in a region
gcloud scheduler jobs list --location=us-central1

# Run a job immediately to test it after creation
gcloud scheduler jobs run daily-report --location=us-central1

# Pause a job (it will not fire until resumed)
gcloud scheduler jobs pause daily-report --location=us-central1

# Resume a paused job
gcloud scheduler jobs resume daily-report --location=us-central1

# View last execution status and full configuration
gcloud scheduler jobs describe daily-report --location=us-central1
Tip

Use gcloud scheduler jobs run to test a new job without waiting for its next scheduled time. If the job fails, fix the issue and run it again before the schedule fires unexpectedly.

Triggering Cloud Run Jobs on a schedule

Cloud Run Jobs are the standard way to run batch work in GCP: a container that starts, performs a defined task, and exits. Cloud Scheduler is the standard way to trigger them on a schedule. The pattern is:

  1. Build a container that performs the batch work (export, report, migration)
  2. Deploy it as a Cloud Run Job
  3. Create a Cloud Scheduler job that calls the Cloud Run Jobs API to start an execution

The service account needs roles/run.developer (which includes run.jobs.run) to start Cloud Run Job executions.

# Deploy a Cloud Run Job for nightly batch work
gcloud run jobs create nightly-export \
  --image=us-central1-docker.pkg.dev/PROJECT_ID/my-repo/exporter:latest \
  --region=us-central1 \
  --tasks=1 \
  --task-timeout=3600s

# Create the service account if it does not already exist
gcloud iam service-accounts create scheduler-sa \
  --display-name="Cloud Scheduler invoker"

# Grant it permission to start Cloud Run Job executions
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member=serviceAccount:scheduler-sa@PROJECT_ID.iam.gserviceaccount.com \
  --role=roles/run.developer

# Schedule it to run at 01:00 UTC every night
gcloud scheduler jobs create http run-nightly-export \
  --schedule="0 1 * * *" \
  --uri="https://run.googleapis.com/v2/projects/PROJECT_ID/locations/us-central1/jobs/nightly-export:run" \
  --http-method=POST \
  --oauth-service-account-email=scheduler-sa@PROJECT_ID.iam.gserviceaccount.com \
  --time-zone="UTC" \
  --location=us-central1
Note

The Cloud Run Jobs API uses OAuth, not OIDC. Use —oauth-service-account-email when calling Google APIs directly, and —oidc-service-account-email when calling your own private Cloud Run service endpoints. Mixing these up is a common source of HTTP 401 errors.

Retry behaviour and idempotency

If the target returns a non-2xx HTTP response or times out, Cloud Scheduler retries according to the policy you configure:

  • Max retry attempts: up to 5 retries after the initial attempt
  • Min and max backoff: how long to wait between retries, starting short and growing
  • Max doublings: how many times to double the backoff before capping at the maximum
  • Attempt deadline: cancel an attempt if the target has not responded within this window
# Create a job with explicit retry and timeout configuration
gcloud scheduler jobs create http daily-report \
  --schedule="0 9 * * *" \
  --uri="https://my-service-xyz.run.app/generate-report" \
  --http-method=POST \
  --time-zone="Europe/London" \
  --location=us-central1 \
  --max-retry-attempts=3 \
  --min-backoff-duration=5s \
  --max-backoff-duration=60s \
  --max-doublings=3 \
  --attempt-deadline=30m
At-least-once delivery

Cloud Scheduler guarantees at-least-once delivery. Under rare network conditions, a job may fire more than once per scheduled interval. Always design job handlers to be idempotent: running the same job twice should produce the same result as running it once.

A practical idempotency pattern: before inserting a daily report record, check whether a record for today already exists. If it does, skip the insert and return HTTP 200. This prevents duplicate records whether the duplication comes from a retry, a manual test run, or an at-least-once delivery event.

Cloud Scheduler vs Cloud Tasks

These two services are often confused because both involve triggering HTTP endpoints. They solve different problems.

Cloud SchedulerCloud Tasks
Trigger sourceA cron schedule (the clock)Application code (an event or user action)
Triggers per intervalOne per schedule fireAs many as your application enqueues
Rate limitingNo — fires immediately on scheduleYes — per-queue dispatch rate
DeduplicationNo per-execution deduplicationYes — by task name within a 4-hour window
Future schedulingFixed recurring schedule onlyPer-task schedule_time field
Typical use caseNightly export, daily report, API pollingEmail after signup, async image processing
Decision rule

If the trigger is a clock and the schedule is fixed, use Cloud Scheduler. If your application decides when and how many tasks to create, use Cloud Tasks.

Common architecture patterns

Pattern 1: Scheduler triggers a Cloud Run service endpoint

Cloud Scheduler sends an authenticated POST request to a private Cloud Run service. The service runs the job logic, returns HTTP 200, and the scheduler records success. Good for short jobs that complete within the Cloud Run request timeout.

Pattern 2: Scheduler publishes to Pub/Sub, subscribers process

Cloud Scheduler publishes a message to a Pub/Sub topic. One or more subscribers receive it and do the work. Useful when multiple services need to react to the same scheduled event, or when you want the processing logic fully decoupled from the schedule.

Pattern 3: Scheduler triggers a Cloud Run Job

Cloud Scheduler calls the Cloud Run Jobs API. A Cloud Run Job container starts, runs to completion, and exits. The preferred approach for longer batch jobs: not constrained to a request timeout, and able to scale across multiple parallel tasks.

Common beginner mistakes

  1. Not setting the time zone. Cloud Scheduler defaults to UTC. A job at 0 9 * * * without —time-zone runs at 09:00 UTC. For a team expecting 09:00 London time in summer, that is one hour late. Always set —time-zone explicitly.

  2. Not making the job handler idempotent. Cloud Scheduler retries on non-2xx responses and provides at-least-once delivery. If the handler creates a database record without checking for duplicates first, you will eventually get double records. Always verify whether the work was already done before doing it again.

  3. Not testing the job after creating it. A wrong cron expression or misconfigured URL will not surface until the next scheduled trigger. Run gcloud scheduler jobs run JOB_NAME —location=REGION immediately after creation to verify it works.

  4. Triggering a public endpoint for sensitive batch work. A public endpoint can be called by anyone who knows the URL. Use a private Cloud Run service with OIDC authentication so only Cloud Scheduler can trigger it.

  5. Using Scheduler when Cloud Tasks is the better fit. If you need to process individual items — one per user, one per order — at a controlled rate, Cloud Tasks is the right tool. Cloud Scheduler fires one trigger per interval; it is not a queue.

  6. Forgetting to set attempt-deadline for long-running jobs. The default attempt deadline may cancel a job before it completes. Set —attempt-deadline to a value higher than the expected execution time of the target.

Frequently asked questions

What is Cloud Scheduler used for?

Cloud Scheduler runs jobs on a recurring cron schedule without any always-on infrastructure. Common uses include generating daily reports, running nightly data exports, polling external APIs, triggering cleanup jobs, and starting Cloud Run Jobs or Pub/Sub-driven workflows on a fixed interval. It replaces VM-hosted cron daemons for most cloud-native use cases.

Can Cloud Scheduler call a private Cloud Run service?

Yes. Use --oidc-service-account-email to have Cloud Scheduler generate an OIDC token and attach it to the Authorization header. The service account must have roles/run.invoker on the target service. The Cloud Run service should not allow unauthenticated access — the OIDC token is the only permitted entry point.

Does Cloud Scheduler retry failed jobs?

Yes. If the target returns a non-2xx HTTP response or times out, Cloud Scheduler retries according to the policy you configure: maximum retry count (up to 5), minimum and maximum backoff duration, and maximum doublings for exponential backoff. Cloud Scheduler guarantees at-least-once delivery, so design job handlers to be idempotent — running the same job twice should produce the same result as running it once.

What is the difference between Cloud Scheduler and a cron job on a VM?

A cron job on a VM requires the VM to stay running 24 hours a day. If the VM reboots or has a maintenance event, the job is missed. Cloud Scheduler is fully managed: no VM to maintain, retries on failure, and execution logs included automatically. You pay per job configuration, not for always-on compute.

Should I use Cloud Scheduler or Cloud Tasks?

Cloud Scheduler is for jobs that run on a fixed recurring schedule — every day at 09:00, every 15 minutes, first of the month. Cloud Tasks is for individual units of work created dynamically by application code, where you need rate limiting, per-task deduplication, or variable scheduling. Use Cloud Scheduler when the trigger is a clock. Use Cloud Tasks when your application decides when and how many tasks to create.

Last verified: 22 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.