Skip to main content

Render Cron Job Not Running: A Diagnosis Order That Finds It Fast

Your Render cron job is not running and the Runs page is empty. Work through the causes in the order that actually finds them, then get alerted next time.

FLAREWARDEN
FlareWarden Team
8 min read

An empty Runs page means one of two very different things: the job never started, or it started and Render never told you it finished. Those have different causes and different fixes, and most of the time lost on this is spent debugging the second while assuming the first. Check the Runs page first, then work down.

Start Here: Is the Runs Page Empty or Full of Failures?

Open the cron job in the Render Dashboard and go to its Runs page. It shows run history and logs for each run, and it splits the problem in half immediately.

  • Runs listed, all failing. The schedule is fine. Your command is exiting non-zero, and the logs on each run will say why. Skip to the environment section below.
  • Runs listed, but fewer than expected. The schedule fired sometimes. Read the single-run guarantee section, which is the usual cause and the least-known one.
  • No runs at all, ever. The service was never eligible to run. Read the next two sections.

While you’re there, use Trigger Run to fire the job manually. If the manual run succeeds, your command works and the problem is scheduling. If it fails the same way, the problem is your command or its environment, and you’ve just cut the search space in half without waiting for the next scheduled slot.

The Two Causes of “It Has Never Run Once”

Two conditions produce a Render cron job not running even once, and both are configuration rather than code.

Your cron job is on a plan that can’t run it. Render’s free instances cover static sites, web services, Postgres, and Key Value only. Render’s own documentation is explicit that other service types don’t support free instances, and cron jobs carry a minimum charge of $1 per month per service, billed by the second of active running time (checked September 2026). A cron job that was never on a paid compute plan is a cron job that has never run.

Your schedule is in UTC and you wrote it in local time. Every day and time range in a Render cron expression is UTC. 0 2 * * * is 2 a.m. UTC, which is 9 p.m. or 10 p.m. the previous day on the US East Coast, and 3 a.m. in Berlin. If you’re checking for output at what you think is 2 a.m. and finding none, you may simply be looking hours before or after the job actually ran. This is the single most common false alarm in this category, and it costs people entire evenings.

The fastest way to rule it out: convert your intended local time to UTC, compare it to what’s in the Schedule field, and check the Runs page timestamps rather than your own logs.

The Cause Almost Nobody Checks: The Single-Run Guarantee

Render guarantees at most one run of a given cron job is active at a time. That protection has a consequence that reads like a bug if you don’t know about it:

If a run is still active when the next scheduled run comes due, Render delays the next scheduled run until the active one finishes.

So a job that hangs doesn’t fail loudly. It quietly eats the next run, and the one after that, for as long as it stays alive. A nightly job that hung on Tuesday at 2 a.m. and is still holding a database connection open on Thursday has now missed two nights, and nothing in your inbox says so. Render stops an active run after 12 hours, so the ceiling on the damage is bounded, but 12 hours is comfortably enough to swallow an hourly job’s entire day.

Two things follow from this. First, when runs are missing but not failing, look at the duration of the last successful run before you look at anything else. Second, always set a timeout inside the job itself rather than relying on the 12-hour stop. If your backup normally takes four minutes, timeout 900 around it turns a silent two-day gap into one visible failure.

When the Command Works Locally and Fails on Render

Cron jobs are separate services, and their environment is separate too. The classic version of this is a job that runs fine when you shell into the web service and fails on its own schedule, because the two don’t share configuration by default.

Work through these in order:

  1. Environment variables. Cron jobs set their own, like any other Render service. A DATABASE_URL or API key that lives on your web service is not automatically present here. Environment groups exist precisely so several services can share one set, and moving your shared values into a group is usually the permanent fix rather than copying them twice.
  2. The command must exit. Render’s docs are blunt about this: use a command that exits when it’s done. A process that stays alive holds the run open, and the single-run guarantee above then starts eating subsequent schedules.
  3. No persistent disk. Cron jobs can’t provision or access a persistent disk. Anything writing to a path you expected to survive between runs is writing into the void, and a job whose first step is reading yesterday’s state file will fail on a filesystem that resets every run.
  4. Docker images are pulled fresh each run and not retained. If you deploy by image, the run pulls before executing. A registry credential that has expired shows up as a run that fails before your code executes at all.
  5. Builds do not interrupt runs. Pushing a fix mid-run doesn’t apply it to the run in flight. A new build affects future runs only, so a fix you pushed at 2:05 was never going to rescue the 2:00 execution.

Fixing the Real Problem: You Found Out By Looking

Everything above is diagnosis after the fact, and it all starts the same way: you happened to check. The reason “my cron job didn’t run” is such a common search is that a job which doesn’t run produces nothing at all. No error page, no 500, no alert. Silence is indistinguishable from success, and the discovery is usually a customer asking where last week’s report went.

Uptime monitoring can’t help, because there’s no endpoint to poll. The job has to tell you it ran, and something has to notice when it doesn’t. That’s what cron monitoring does, and it’s push-based for a reason: your job makes an outbound HTTPS request, so nothing needs inbound access to Render and there’s nothing to install.

In FlareWarden you create a cron monitor, tell it how often the job runs and how long to wait before calling it late, and add one line to the command:

your-command && curl -fsS -m 10 https://app.flarewarden.com/ping/YOUR-TOKEN

If the expected ping doesn’t arrive within the grace period, the monitor moves to Late and alerts. That single line covers the missing-run case, which is most of this article.

For the hung-run case specifically, ping /start when the job begins and the base URL when it finishes. With a maximum run duration configured, a job that starts and never completes is reported as a hung job rather than waiting for the next missed run to imply it. Given how Render’s single-run guarantee turns one hang into a string of skipped schedules, that distinction is worth the extra line. There’s also /fail, which takes an error message in the body, so a job that catches its own exception can say what went wrong instead of just going quiet.

Set the grace period generously. Render builds and starts a fresh instance for each run, so a job scheduled at 2:00 may not begin executing for a minute or two. A grace period shorter than your cold start produces false alarms, which is a worse outcome than the silence you started with.

The Same Trap, Other Platforms

A Render cron job not running is a specific case of a general problem: scheduled work is the one part of a system that fails without producing a symptom. The specifics differ by platform, but the shape of the fix is identical. Vercel cron jobs fail silently under their own rules, GitHub Actions scheduled workflows get delayed or dropped under load, and Kubernetes CronJobs have concurrency and deadline settings with the same one-hang-eats-the-schedule behaviour Render has.


Key Takeaways

  • Read the Runs page before anything else. Empty, thin, and failing are three different problems, and the page tells you which one you have in five seconds.
  • A Render cron job not running has never once been on a free instance. Free covers static sites, web services, Postgres, and Key Value. Cron jobs need a paid compute plan.
  • Cron expressions are UTC, always. A large share of “it didn’t run” reports are people checking at the wrong hour.
  • A hung run delays the next scheduled one. The single-run guarantee means one job that never exits quietly eats every schedule behind it, up to Render’s 12-hour stop.
  • Cron jobs don’t inherit your web service’s environment. Use environment groups instead of copying variables between services.
  • Silence needs its own alarm. Nothing about a job that never ran generates an error, so the job has to check in and something has to notice when it stops.

Want to hear about the run that didn’t happen, instead of finding out next week? Start monitoring free with FlareWarden — cron and heartbeat monitors, 15 monitors, no credit card required.