Cloud Scheduler is essentially a managed cron service that can trigger HTTP endpoints or publish messages to Pub/Sub topics.

Here’s how you can set up a Cloud Run job to be triggered on a schedule:

1. Create Your Cloud Run Job

First, you need a container image that performs the task you want to schedule. This container should listen for HTTP requests on a specific port (defaulting to 8080) and execute your job logic when a request is received.

Let’s say you have a simple Python script main.py:

import os
from flask import Flask

app = Flask(__name__)

@app.route('/', methods=['POST'])
def run_job():
    print("Job triggered!")
    # Your job logic here
    return 'Job executed successfully', 200

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8080))
    app.run(host='0.0.0.0', port=port)

You’ll need a Dockerfile to build this:

FROM python:3.9-slim

WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

CMD ["python", "main.py"]

And a requirements.txt file:

Flask

Build and push this image to Google Container Registry (GCR) or Artifact Registry. For example:

docker build -t gcr.io/your-gcp-project-id/my-scheduled-job:latest .
docker push gcr.io/your-gcp-project-id/my-scheduled-job:latest

Now, deploy this as a Cloud Run service. Crucially, you’ll deploy it as a service, not a job, because Cloud Scheduler targets HTTP endpoints.

gcloud run deploy my-scheduled-job-service \
  --image gcr.io/your-gcp-project-id/my-scheduled-job:latest \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated # For simplicity in this example; consider authentication for production

2. Create a Cloud Scheduler Job

Once your Cloud Run service is deployed, you can create a Cloud Scheduler job to trigger it.

gcloud scheduler jobs create http my-cron-job \
  --schedule "*/5 * * * *" \
  --uri "https://my-scheduled-job-service-abcdef12345-uc.a.run.app" \
  --http-method POST \
  --location us-central1 \
  --message-body "trigger" # Optional: send a body

Let’s break down the command:

  • my-cron-job: The name of your Cloud Scheduler job.
  • --schedule "*/5 * * * *": This is a standard cron syntax. This example means "every 5 minutes."
  • --uri: This is the URL of your Cloud Run service. You can get this from the gcloud run deploy output or by running gcloud run services describe my-scheduled-job-service --platform managed --region us-central1 --format 'value(status.url)'.
  • --http-method POST: Cloud Scheduler will send a POST request to your Cloud Run service. Your Cloud Run application should be set up to handle POST requests.
  • --location: The GCP region where you want to create the Cloud Scheduler job. This should ideally be the same region as your Cloud Run service.
  • --message-body "trigger": This is optional. If your Cloud Run service needs to know why it was triggered, you can send a body.

3. How it Works Under the Hood

When the scheduled time arrives, Cloud Scheduler makes an HTTP POST request to the specified URI of your Cloud Run service. Your Cloud Run service, which is always running (or scales up to one instance if it was scaled to zero), receives this request. The handler function in your application (run_job in our example) is invoked, and it executes your desired logic.

The key here is that you’re not deploying a "job" in the Cloud Run Jobs sense (which runs to completion and then stops). You’re deploying a Cloud Run service that’s always available to receive HTTP requests, and Cloud Scheduler is the mechanism that invokes that service on a schedule.

4. Handling Authentication

In the example above, --allow-unauthenticated was used for simplicity. In a production environment, you’ll want to secure your Cloud Run service. Cloud Scheduler can be configured to send an OIDC token for authentication.

First, grant the Cloud Scheduler service account the run.invoker role on your Cloud Run service:

# Find the Cloud Scheduler service account
gcloud projects describe your-gcp-project-id --format 'value(serviceAccount.defaultServiceAccount)'
# Or, if it's a custom service account:
# SCHEDULER_SA="your-scheduler-service-account@your-gcp-project-id.iam.gserviceaccount.com"

# Grant the role
gcloud run services add-iam-policy-binding my-scheduled-job-service \
  --member="serviceAccount:your-scheduler-service-account@your-gcp-project-id.iam.gserviceaccount.com" \
  --role="roles/run.invoker" \
  --region=us-central1 \
  --platform=managed

Then, update your Cloud Scheduler job to include the OIDC token:

gcloud scheduler jobs update http my-cron-job \
  --schedule "*/5 * * * *" \
  --uri "https://my-scheduled-job-service-abcdef12345-uc.a.run.app" \
  --http-method POST \
  --location us-central1 \
  --oidc-service-account-email "your-scheduler-service-account@your-gcp-project-id.iam.gserviceaccount.com" \
  --oidc-token-audience "https://my-scheduled-job-service-abcdef12345-uc.a.run.app"
  • --oidc-service-account-email: The service account Cloud Scheduler will use to generate the token.
  • --oidc-token-audience: The audience for the OIDC token, which should match your Cloud Run service URL.

Your Cloud Run application will receive a request with an Authorization: Bearer <token> header. You can verify this token within your application if needed, but often, just having the authenticated request is enough for Cloud Run’s IAM to authorize the invocation.

5. What Happens if the Job Fails?

Cloud Scheduler has built-in retry mechanisms. You can configure the number of retries and the backoff duration. If your Cloud Run service returns a non-2xx status code, Cloud Scheduler will attempt to retry the request based on your configuration.

You’ll see execution logs in both Cloud Scheduler and Cloud Run.

The next step is often to manage the state of your scheduled tasks, perhaps by triggering a Cloud Tasks queue for more complex workflows or handling distributed state.

Want structured learning?

Take the full Cloud-run course →