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.
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:
- You write a cron expression, such as
0 9 * * *(every day at 09:00) - You pick a target: an HTTP URL, a Pub/Sub topic, or a Cloud Run Job
- Google fires the trigger on the schedule, with retries if the target fails
- 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.
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.comHTTP 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-central1Pub/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-central1Cloud 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:000 9 * * 1— every Monday at 09:00*/15 * * * *— every 15 minutes0 0 1 * *— first day of every month at midnight0 8-18 * * 1-5— every hour from 08:00 to 18:00, Monday to Friday30 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.
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
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-central1The 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-central1Use 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:
- Build a container that performs the batch work (export, report, migration)
- Deploy it as a Cloud Run Job
- 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-central1The 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=30mCloud 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 Scheduler | Cloud Tasks | |
|---|---|---|
| Trigger source | A cron schedule (the clock) | Application code (an event or user action) |
| Triggers per interval | One per schedule fire | As many as your application enqueues |
| Rate limiting | No — fires immediately on schedule | Yes — per-queue dispatch rate |
| Deduplication | No per-execution deduplication | Yes — by task name within a 4-hour window |
| Future scheduling | Fixed recurring schedule only | Per-task schedule_time field |
| Typical use case | Nightly export, daily report, API polling | Email after signup, async image processing |
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
Not setting the time zone. Cloud Scheduler defaults to UTC. A job at
0 9 * * *without—time-zoneruns at 09:00 UTC. For a team expecting 09:00 London time in summer, that is one hour late. Always set—time-zoneexplicitly.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.
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=REGIONimmediately after creation to verify it works.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.
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.
Forgetting to set attempt-deadline for long-running jobs. The default attempt deadline may cancel a job before it completes. Set
—attempt-deadlineto a value higher than the expected execution time of the target.
Summary
- Cloud Scheduler is GCP’s managed cron service: define a schedule and a target, and Google fires it for you
- Targets include HTTP/HTTPS endpoints, Pub/Sub topics, and the Cloud Run Jobs API
- Always set
—time-zoneexplicitly — the default is UTC - Use
—oidc-service-account-emailandroles/run.invokerto call private Cloud Run services - Use
—oauth-service-account-emailandroles/run.developerto trigger Cloud Run Jobs via the API - Cloud Scheduler guarantees at-least-once delivery — design job handlers to be idempotent
- Test every job immediately after creation with
gcloud scheduler jobs run - For per-item async work with rate limiting, use Cloud Tasks instead
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.