Cloud Tasks in GCP: Queues, Retries, Scheduling Explained

Cloud Tasks solves a specific problem: your web request needs to trigger slow work (sending an email, calling a third-party API, generating a report) but you do not want the user to wait for it. Push a task to a queue, respond immediately, and let the queue deliver the work to a handler at a controlled rate with automatic retries. Unlike Pub/Sub, Cloud Tasks is not for broadcasting events to multiple consumers. Each task goes to exactly one target endpoint, and your application controls when and how fast tasks are dispatched.

Simple explanation

Think of Cloud Tasks as a to-do list between your app and a worker service.

  1. A user submits a signup form. Your app needs to send a welcome email, but that involves a third-party mail API and could take 500ms or more.
  2. Instead of waiting, your app adds “send welcome email to user@example.com” to a queue and immediately returns a 200 response.
  3. The user sees a fast confirmation page.
  4. Cloud Tasks delivers the queued job to your email worker service in the background, at the dispatch rate you configured.
  5. If the worker fails because the mail API is temporarily down, Cloud Tasks automatically retries with exponential backoff.

The key difference from broadcasting: you know exactly which service will do the work. Cloud Tasks delivers every task to one specific HTTP endpoint. If you need five different services to react to the same event, that is a job for Pub/Sub, not Cloud Tasks.

Analogy

Cloud Tasks is like leaving a note on a specific colleague’s desk. You write clear instructions, leave the note, and walk away. Your colleague works through the notes at their own pace, and if they miss one, someone follows up. There is only one colleague who gets each note, and you chose them specifically.

How it works

The lifecycle of a task from creation to completion:

  1. Application creates a task. Your code calls the Cloud Tasks API with an HTTP request body, target URL, headers, and optional metadata.
  2. Task is added to a queue. The queue is named and has its own dispatch rate, concurrency limit, and retry policy. Multiple tasks sit in the queue simultaneously waiting to be dispatched.
  3. Queue dispatches at a controlled rate. Cloud Tasks sends HTTP requests to the target according to the queue’s max-dispatches-per-second and max-concurrent-dispatches settings. This protects the worker from being overwhelmed by a sudden burst.
  4. Target receives the HTTP request. Your worker (typically a Cloud Run service or Cloud Function) receives the task as a standard HTTP POST request and processes it.
  5. Success removes the task. Any 2xx HTTP response tells Cloud Tasks the task is complete. It is removed from the queue.
  6. Failure triggers retries. A non-2xx response or timeout causes Cloud Tasks to retry after a backoff delay. The retry policy (max attempts, backoff duration, max doublings) is configured on the queue.
  7. Scheduled tasks wait. If a schedule_time is set, Cloud Tasks holds the task in the queue and does not dispatch it until that timestamp arrives.
Analogy

The dispatch rate is like a bouncer controlling how many people enter a venue per minute. Even if a thousand people show up at once, the bouncer lets them in steadily so the venue does not get overwhelmed. The queue is the line outside. The venue is your worker service.

Core concepts

  • Queue: a named container for tasks. Dispatch rate, concurrency limits, and retry behaviour are configured at the queue level. All tasks in the same queue share those settings.

  • Task: an HTTP request to be delivered to a target. Contains the URL, HTTP method, headers, body, and optional schedule time.

  • Target / handler: the HTTPS endpoint that receives and processes the task. Most commonly a private Cloud Run service. The handler must return a 2xx response to signal success.

  • Dispatch rate: the maximum number of tasks the queue sends per second (max-dispatches-per-second) and the maximum number of tasks being processed simultaneously (max-concurrent-dispatches).

  • Retry policy: what happens when a task fails. Configured with —max-attempts, —min-backoff, —max-backoff, and —max-doublings. Without a limit, failed tasks retry indefinitely.

  • Schedule time: an optional timestamp set when creating a task. Cloud Tasks holds the task until that time before dispatching. Useful for time-delayed notifications or follow-up actions hours or days later.

  • Deduplication: if you name a task, Cloud Tasks rejects any task with the same name in the same queue within a 4-hour window. Prevents double-processing caused by network retries or accidental double-submissions from the creating application.

  • Idempotency: the property of a handler where running it twice produces the same result as running it once. Required because Cloud Tasks may deliver the same task more than once due to retries.

When to use this

Cloud Tasks fits situations where your application needs to hand off slow work to a specific service without blocking on the result:

  • Sending emails after signup. Return a fast response to the user, then send the welcome email in the background.
  • Processing file uploads. Accept the upload immediately, dispatch a task to resize images or run a virus scan after the HTTP response completes.
  • Calling slow third-party APIs. Queue the API call to a worker so your main request-response cycle stays fast and isolated from third-party latency.
  • Background report generation. Trigger a report-building worker for a request that could take seconds or minutes.
  • Delayed follow-up actions. Schedule a reminder task 24 hours after a user action, or a follow-up 7 days into a trial period.
  • Smoothing traffic spikes. Protect a downstream service from burst load by queuing work at a rate the worker can reliably handle.

When not to use Cloud Tasks

Cloud Tasks is the wrong choice in these situations:

  • Fan-out to multiple consumers. Each task goes to one target. If multiple services must react to the same event, use Pub/Sub, which delivers each message to all subscribers independently.
  • Event broadcasting. If you want to notify consumers that something happened without controlling who handles it, use event-driven architecture with Pub/Sub as the event bus.
  • Recurring scheduled work. If you want to run something every day at 09:00 or every 15 minutes, use Cloud Scheduler, which manages cron-style triggers without needing application code to create tasks.
  • Long-running heavy compute. If the work takes many minutes and requires significant CPU or memory, consider Batch Jobs in GCP for the compute side rather than an HTTP handler backed by Cloud Tasks.

Creating queues and tasks

First, create the queue. The queue settings control how fast tasks are dispatched and what to do when they fail.

# Enable the API
gcloud services enable cloudtasks.googleapis.com

# Create a queue with rate limiting
# max-dispatches-per-second: tasks sent per second
# max-concurrent-dispatches: tasks in-flight at once
gcloud tasks queues create email-queue \
  --location=us-central1 \
  --max-dispatches-per-second=10 \
  --max-concurrent-dispatches=5

# List queues
gcloud tasks queues list --location=us-central1

Then create tasks from your application code. The OIDC token authenticates the request so only Cloud Tasks can call your handler, not anyone who knows the URL. This is the standard pattern for Cloud Run services secured by IAM.

# Create a task from application code (Python)
from google.cloud import tasks_v2
import json

client = tasks_v2.CloudTasksClient()
parent = client.queue_path("my-project", "us-central1", "email-queue")

task = {
    "http_request": {
        "http_method": tasks_v2.HttpMethod.POST,
        "url": "https://my-worker-service.run.app/send-email",
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({
            "to": "user@example.com",
            "subject": "Welcome!"
        }).encode(),
        # OIDC authenticates as a service account so the worker
        # can verify the request came from Cloud Tasks, not a stranger
        "oidc_token": {
            "service_account_email": "tasks-sa@PROJECT_ID.iam.gserviceaccount.com"
        }
    }
}

response = client.create_task(parent=parent, task=task)
print(f"Created task: {response.name}")
Note

The service account used in the OIDC token needs roles/run.invoker on the target Cloud Run service. The worker validates the token on every incoming request, rejecting anything that did not come through Cloud Tasks.

Scheduling tasks for the future

Cloud Tasks can hold a task and dispatch it at a specific time in the future. This is useful for time-delayed actions triggered by a user event but executed hours or days later.

from google.cloud import tasks_v2
from google.protobuf import timestamp_pb2
import datetime

client = tasks_v2.CloudTasksClient()
parent = client.queue_path("my-project", "us-central1", "email-queue")

# Schedule a reminder 24 hours from now
schedule_time = datetime.datetime.utcnow() + datetime.timedelta(hours=24)
timestamp = timestamp_pb2.Timestamp()
timestamp.FromDatetime(schedule_time)

task = {
    "http_request": {
        "http_method": tasks_v2.HttpMethod.POST,
        "url": "https://my-worker-service.run.app/send-reminder",
        "body": b'{"user_id": "123", "type": "day-1-reminder"}',
    },
    "schedule_time": timestamp
}

client.create_task(parent=parent, task=task)
Tip

Scheduled tasks work well for SaaS onboarding flows: create a day-1 reminder at signup, a day-3 tips email when the user first logs in, and a day-7 check-in after their first feature use. Each task is created at the moment the triggering event happens, not by polling a database on a cron.

Unlike Cloud Scheduler, which runs jobs on a repeating cron schedule, Cloud Tasks scheduling is one-shot: a specific task created by your code fires once at the requested time. The two tools complement each other. Cloud Scheduler for “run this every Monday at 09:00”. Cloud Tasks for “send this specific user a reminder at 14:35 tomorrow”.

Task deduplication

Naming a task enforces deduplication within a 4-hour window. If your application submits the same task twice (due to a network retry or a bug), the second submission is rejected with ALREADY_EXISTS. This prevents the same work from being queued and processed twice.

client = tasks_v2.CloudTasksClient()
parent = client.queue_path("my-project", "us-central1", "email-queue")

# Name the task so duplicate submissions are rejected within 4 hours
task_name = client.task_path(
    "my-project", "us-central1", "email-queue",
    "welcome-email-user-12345"
)

task = {
    "name": task_name,
    "http_request": {
        "http_method": tasks_v2.HttpMethod.POST,
        "url": "https://my-worker-service.run.app/send-email",
        "body": b'{"user_id": "12345", "type": "welcome"}',
    }
}

try:
    response = client.create_task(parent=parent, task=task)
    print(f"Task created: {response.name}")
except Exception as e:
    # ALREADY_EXISTS if this task name was used in the last 4 hours
    print(f"Duplicate task, skipping: {e}")
Note

Deduplication prevents double-submission by the creating application, but does not prevent Cloud Tasks from retrying a task after a handler failure. Your handler still needs to be idempotent to handle retry-driven re-delivery safely.

Retry behaviour and idempotency

If a task handler returns a non-2xx HTTP response or times out, Cloud Tasks retries it. The queue’s retry policy controls how many times it retries and how long to wait between attempts.

# Configure retry behaviour on the queue
gcloud tasks queues update email-queue \
  --location=us-central1 \
  --max-attempts=5 \
  --max-retry-duration=3600s \
  --min-backoff=10s \
  --max-backoff=300s \
  --max-doublings=3
Warning

If —max-attempts is not set, Cloud Tasks retries indefinitely. A handler with a persistent bug will keep retrying forever, consuming queue resources and generating endless noise in Logs Explorer. Always set a sensible maximum attempt count.

Because retries are automatic, your handler must be idempotent. If Cloud Tasks delivers the same task twice (once on the first attempt, once on a retry), your handler should produce the same result both times. A non-idempotent handler sends duplicate emails, double-charges payments, or inserts duplicate rows.

Analogy

Idempotency is like a light switch: flipping it on twice is the same as flipping it on once. The end state is identical. Design your handlers the same way. Sending a “welcome” email to user 123 should check whether that email was already sent before sending it again, so a second delivery produces the same outcome as the first.

Practical approaches to idempotency:

  • Use an upsert (INSERT OR REPLACE, ON CONFLICT DO UPDATE) instead of a plain insert
  • Check whether the work was already completed before doing it. Use a task name or entity ID as a deduplication key in your database.
  • Make state changes conditional: only update a record if it is still in the expected prior state

Cloud Tasks vs Pub/Sub

This is the most common source of confusion. Both queue work for async processing, but they are designed for different routing models. See also the dedicated Pub/Sub vs Cloud Tasks comparison.

Cloud TasksPub/Sub
Delivery targetOne specific HTTP endpointAny number of subscribers
Fan-outNo — one target per taskYes — all subscribers receive every message
Rate limitingYes — per-queue dispatch rateNo built-in rate limiting
Future schedulingYes — schedule_time fieldNo
DeduplicationYes — by task name (4-hour window)No
Caller controls targetYes — caller specifies the URLNo — consumers subscribe independently

Choose Cloud Tasks when the creating application knows exactly which service handles the work and needs control over dispatch rate, scheduling, or deduplication.

Choose Pub/Sub when an event should notify multiple consumers independently, or when the publisher should not know or care who is listening. Read Pub/Sub Push vs Pull to understand the delivery modes once you have chosen Pub/Sub.

Cloud Tasks vs Cloud Scheduler

Both involve scheduling future work, but they operate at different levels.

Cloud TasksCloud Scheduler
TriggerApplication code creates individual tasksCron expression — time-based trigger
RecurrenceNo — each task fires onceYes — repeating schedule
Rate limitingYes — per-queue settingsNo
Queue of many itemsYes — thousands of tasks per queueNo — one trigger per job
Use caseBackground work from individual user actionsBatch jobs, reports, recurring maintenance

Choose Cloud Tasks when your application creates individual units of work in response to events: a new user, an uploaded file, a completed checkout.

Choose Cloud Scheduler when the work runs on a predictable time-based schedule regardless of user activity: a nightly batch job, an hourly data export.

The two work well together. Cloud Scheduler fires a Cloud Run endpoint on a cron schedule. That endpoint reads a database and enqueues thousands of Cloud Tasks, one per user needing a weekly digest email. Cloud Scheduler triggers the fan-out; Cloud Tasks handles the per-user dispatch at a controlled rate.

Common mistakes

  1. Confusing Cloud Tasks with Pub/Sub. Cloud Tasks is not an event bus. Each task goes to exactly one specific target. If you want multiple workers to receive the same task, you need Pub/Sub, not Cloud Tasks.

  2. Building non-idempotent handlers. Cloud Tasks retries on non-2xx responses. The same task will be delivered more than once. Handlers that insert rows, send emails, or charge payments without checking for prior execution will cause duplicates. Make every handler safe to run twice before writing any other logic.

  3. Using public handlers without authentication. A publicly accessible handler can be called by anyone who knows the URL, bypassing the queue entirely. Use a private Cloud Run service with OIDC authentication so only Cloud Tasks can invoke it.

  4. Setting the dispatch rate too high for the worker. If the queue dispatches faster than the worker can process, requests back up and the worker becomes overloaded or starts returning 429s. Start conservative with —max-dispatches-per-second and increase it based on observed worker latency and error rates.

  5. Forgetting to set a retry limit. Without —max-attempts, Cloud Tasks retries indefinitely. A handler with a persistent bug will keep retrying forever. Set a sensible limit and monitor for tasks exhausting their retries in Logs Explorer.

  6. Using Cloud Tasks for long-running heavy compute. Cloud Tasks dispatches HTTP requests with a fixed timeout. If the work takes many minutes and requires serious CPU or memory, use Batch Jobs in GCP for the compute layer. Cloud Tasks can still enqueue the job reference.

Frequently asked questions

When should I use Cloud Tasks instead of Pub/Sub?

Use Cloud Tasks when the creating application knows exactly which service should handle the work and you need to control the dispatch rate, deduplicate tasks, or schedule work for a future time. Cloud Tasks delivers each task to one specific HTTP endpoint. Use Pub/Sub when multiple consumers need to react to the same event independently. A simple rule: Cloud Tasks for offloading slow work to a specific worker; Pub/Sub for broadcasting events to consumers you may not even control.

Can Cloud Tasks run jobs at a future time?

Yes. Set the schedule_time field when creating a task. Cloud Tasks holds the task until that timestamp arrives. Useful for time-delayed actions: sending a reminder 24 hours after signup, triggering a follow-up 7 days into a trial. The task is retried normally if the target fails when the scheduled time arrives.

Does Cloud Tasks guarantee exactly-once delivery?

No. Cloud Tasks provides at-least-once delivery. If the handler returns a non-2xx response or times out, Cloud Tasks retries according to the queue policy. The same task may be delivered more than once. Your handler must be idempotent: running it twice must produce the same result as running it once. Task naming (deduplication by name) helps prevent double-submission from the creating application, but does not prevent retry-driven re-delivery.

Why must Cloud Tasks handlers be idempotent?

Cloud Tasks retries failed tasks automatically. If your handler inserts a database row, sends an email, or charges a payment on the first delivery and then fails, Cloud Tasks will try again. Without idempotency, the second delivery causes a duplicate. Design handlers to check whether the work was already done, use upserts rather than inserts, or use the task name as a deduplication key stored in your database.

What is the difference between Cloud Tasks and Cloud Scheduler?

Cloud Scheduler runs jobs on a fixed cron schedule: every day at 09:00, every Monday, first of the month. Cloud Tasks manages a queue of individual tasks created on demand by application code, with per-task rate limiting and optional scheduling delays. Use Cloud Scheduler when you know the execution time in advance and want a recurring trigger. Use Cloud Tasks when an HTTP request or application event needs to dispatch work asynchronously at a rate the target can handle.

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