# How Render handles scheduled tasks

- Date: 2026-04-01T10:49:54.580Z
- Tags: Platform
- URL: https://render.com/articles/how-render-handles-scheduled-tasks


## Rethinking scheduled task architecture

Scheduled tasks, or cron jobs, are backend operations that run at predefined time intervals. On many platforms, you run a cron daemon inside the same long-running container as your web server. That ties your background work to your user-facing HTTP traffic, so the two compete for the same memory and CPU, and you lose visibility into when and how each job actually ran.

On Render, you decouple scheduled tasks from your web services entirely. A cron job is its own service type, built for isolated execution. Your background jobs run on their own compute, so they don't eat into the memory or CPU your web requests need. The result is a more resilient application and clearer insight into how your jobs behave.

## Cron jobs as first-class services

A Render cron job is a dedicated service type that runs on its own compute, provisioned just for scheduled execution. This isolation matters for stability. When a single web server also handles heavy background work, like database aggregations or batch email dispatch, it takes RAM and compute away from the concurrent HTTP requests it's supposed to serve. Running that work in separate, ephemeral containers removes the contention.

This isolation extends to resource allocation and billing. You can provision specific memory and CPU limits for your cron service independently of the web application tier. Because these are dedicated workloads, execution pricing remains predictable. Billing is prorated by the second based on active running time, so you only pay for the exact compute duration consumed during task execution, subject to a minimum monthly charge of $1 per cron job service. For granular allocation specifics, refer to the [Render pricing documentation](https://render.com/pricing).

```mermaid
graph TD
    subgraph Traditional setup
        A[Web Traffic] --> B(Web Server Container)
        C((Cron Daemon)) -. inside .- B
        B --> D[(Database)]
    end

    subgraph Render isolated architecture
        E[Web Traffic] --> F(Web Service)
        G((Dedicated Cron Service)) -->|Isolated Execution| H[(Database)]
        F --> H
    end
    
    style C fill:#f9a8d4,stroke:#be185d,stroke-width:2px,stroke-dasharray: 5 5
    style G fill:#86efac,stroke:#166534,stroke-width:2px
```
*A conceptual diagram showing how Render isolates scheduled tasks from web services, unlike traditional single-container setups.*

## Defining the schedule as code

You define when a job runs with a standard 5-field cron expression (minute, hour, day of month, month, day of week), the same syntax you already use with Unix cron. By default, Render evaluates every cron expression against Coordinated Universal Time (UTC). Because UTC has no Daylight Saving Time (DST) shifts, your schedules don't drift or run twice when clocks change, so normalize your scheduling logic to UTC before you deploy.

You can create a cron job in the dashboard, but defining it as code is the more durable approach. When you declare the schedule in a [Blueprint](https://render.com/docs/blueprint-spec#cron-jobs) `render.yaml` file, your configuration is version-controlled and stays in sync with the application code it runs against, instead of living in manual dashboard edits.

Here's a simplified cron job definition in a `render.yaml` file:

```yaml pseudocode
services:
  # Simplification: Environment variables omitted for clarity
  - type: cron
    name: daily-db-cleanup
    runtime: node
    buildCommand: npm install
    startCommand: node cleanup.js
    # Standard cron syntax: runs daily at midnight
    schedule: "0 0 * * *"
    plan: starter
```

For production, add environment variables, a specific branch target, and an instance size suited to your workload. This snippet simply shows the shape of the configuration.

## Execution, visibility, and failure handling

A run starts when the current time matches your cron expression. Render provisions a secure, ephemeral container, runs your start command, and tears the container down as soon as the process exits. Render guarantees that at most one run of a given cron job is active at a time. If a previous run is still going when the next interval arrives, Render delays the next run until the active one finishes. Render also stops any run that exceeds 12 hours. For work that needs to run longer than that, or continuously, use a [background worker](https://render.com/docs/background-workers) instead. To test a script without waiting for the next interval, trigger a run manually from the dashboard, though doing so while a run is active cancels the active run first.

Logging and failure handling come from the same isolated model. Your container's standard output (`stdout`) and standard error (`stderr`) flow into Render's [log streams](https://render.com/docs/log-streams), kept separate from your web traffic logs. Failure tracking keys off the process exit code: when your script exits with a non-zero status, Render records the run as failed and can alert you over email or Slack through its [notifications](https://render.com/docs/notifications), with no third-party monitoring to wire up.

This minimal script shows a scheduled task that exits with a clear status code for Render to track:

```javascript pseudocode
async function executeJob() {
  try {
    // Production: add robust error handling, retries, and database transactions
    await performCleanup();
    await closeDb();
    process.exit(0);
  } catch (err) {
    console.error("Job failure:", err);
    await closeDb();
    // Render tracks the standard exit code to determine job success or failure
    process.exit(1);
  }
}
executeJob();
```

For production, make your scripts idempotent and connect them to external logging or alerting if you need complex rollbacks.

## The step up from Heroku Scheduler

Moving from the Heroku Scheduler add-on to Render is a shift from best-effort scheduling to guaranteed scheduling. Heroku Scheduler runs jobs on one-off dynos on a best-effort basis, which Heroku's own documentation notes can mean a job runs late or, in rare cases, not at all. Render's cron jobs are a built-in service type rather than a marketplace add-on, and they run on dedicated compute. That makes them a better fit for batch operations you actually depend on.

## Designing robust scheduled workloads

A few principles keep scheduled jobs reliable as they grow. Keep these in mind when you design your workloads:

* *Avoid intervals shorter than your runtime:* If you schedule a job more frequently than it can finish, the single-run guarantee means Render delays each next run until the active one completes. Pick an interval with room to spare, or split the work.
* *Treat storage as ephemeral:* Cron jobs can't provision or access a persistent disk. Write any data you need to keep to an external database or object storage.
* *Make your jobs idempotent for full reversibility:* A retried or partially completed run shouldn't double-apply its effects. Design your scripts so that running the same job twice doesn't duplicate database writes or leave your application in a conflicting state.

## Frequently asked questions

###### What happens if a cron job is still running when its next scheduled run is due?

Render guarantees at most one active run per cron job. If a run is still in progress when the next interval arrives, Render delays the next run until the current one finishes rather than running them in parallel. If your job regularly overlaps its own schedule, lengthen the interval or break the work into smaller jobs.

###### Is there a limit on how long a cron job can run?

Yes. Render stops any single run that exceeds 12 hours. For work that needs to run longer than that, or continuously, use a background worker instead of a cron job.

###### How does Render know whether a run succeeded or failed?

Render uses your process exit code. An exit code of 0 marks the run as successful, and any non-zero exit code marks it as failed. A failed run can trigger email or Slack notifications, so make sure your script exits non-zero when it hits an unrecoverable error.

###### What time zone do cron schedules use?

All cron expressions are evaluated against UTC, and day and time ranges use UTC as well. UTC has no Daylight Saving Time shifts, which avoids skipped or duplicated runs when clocks change. Convert your intended local schedule to UTC before you deploy.

###### Can a cron job write to a persistent disk?

No. Cron jobs can't provision or access a persistent disk. Persist anything you need to keep to an external database or object storage.

###### How is a cron job billed?

Billing is prorated by the second based on the job's active running time during the month, on whichever instance type you choose. There's a minimum charge of $1 per cron job service per month. Because you only pay for active run time, an infrequent job costs very little.

###### How do I test a cron job without waiting for its schedule?

Trigger a run manually from the job's page in the Render Dashboard. This runs the job immediately using your current configuration. If a run is already active when you trigger one manually, Render cancels the active run first and then starts the new one.

