CronDrive

Cron job monitoring

Quickstart

Or let an AI agent like Claude Code do it for you: see Set up from an AI agent.

  1. Sign up for CronDrive.
  2. Create a new job with the cron expression of your job.

    Example: the new job form
    Schedule

    To recognize it in your list and in the alerts.

    The same five fields as in your crontab: minute, hour, day, month and weekday.

    It's 22:14 there now (UTC−03:00).

    Which time zone do I choose?

    The time zone of the server where the crontab runs, which may not be yours. To see it, run this on the server:

    timedatectl | grep "Time zone"

    If the crontab sets CRON_TZ, choose that one.

    Every day at 03:00

    Next runs
    • Thu 22 Oct, 03:00
    • Fri 23 Oct, 03:00
    • Sat 24 Oct, 03:00
    • Sun 25 Oct, 03:00
    • Mon 26 Oct, 03:00
    Alerts

    A run that fails alerts as soon as its end call arrives. These are the minutes to wait, from the time it was due, when a call doesn't arrive.

    Leave it empty if you only add the call for the end.

    For the run of Thu 22 Oct, 03:00: an alert if it hasn't started by 03:05 or hasn't finished by 04:00.

    Needs the start call. At most 3 of these alerts in 24 hours; the history always shows the overlaps.

    The job page shows the line to paste in your crontab:

    Example: the job page

    Nightly backup

    Monitoring 0 3 * * * Every day at 03:00 America/Santiago

    Last run
    On time Tue 20 Oct, 03:00
    Average runtime
    12 minutes
    Next run
    Thu 22 Oct, 03:00 then Fri 23 Oct, 03:00 and Sat 24 Oct, 03:00

    Alerts when a run hasn't started 5 minutes after it was due, or hasn't finished after 60 minutes.

    Your crontab line

    0 3 * * * curl -fsS -m 10 https://job.crondrive.com/uG2Exw/start/$$; /path/to/job.sh; curl -fsS -m 10 https://job.crondrive.com/uG2Exw/end/$?/$$

    Run crontab -e and replace /path/to/job.sh with your command.

  3. Add the tracking calls around your command. For example, on Linux:

    Before:

    0 */2 * * * partial_backup.sh

    After:

    0 */2 * * * curl -fsS -m 10 https://job.crondrive.com/uG2Exw/start/$$; partial_backup.sh; curl -fsS -m 10 https://job.crondrive.com/uG2Exw/end/$?/$$

    The end call always runs, with the command's exit code ($?), so a failed run alerts right away. $$ pairs the end with its start. The page of each job shows its line, ready to copy.

  4. Turn on the monitoring of the job. New jobs start paused: the job page shows when the first call arrives, so you can check the line before any alert is sent.
  5. You get an alert by email (and Slack, if you connect it) when a run fails, or does not start or does not finish on time. The history of the job keeps every run and alert:

    Example: the history of a job that didn't run one night

    Times in America/Santiago.

    Due
    Tue 20 Oct, 03:00 On time Started 03:00, finished 03:11, took 11 min.
    Mon 19 Oct, 03:00 Didn't run Alerts sent at 03:05 and 04:00.
    Sun 18 Oct, 03:00 On time Started 03:00, finished 03:14, took 14 min.
    Sat 17 Oct, 03:00 On time Started 03:00, finished 03:12, took 12 min.

How CronDrive works

You tell CronDrive when your job or script starts (optional) and ends.

To report that your job started, call https://job.crondrive.com/{token}/start.

To report that your job ended, call https://job.crondrive.com/{token}/end/{exit code} with its exit code.

CronDrive sends you an alert when the exit code is not 0, or when it does not receive a tracking call in time. You choose how long to wait before alerting, and you can pause the alerts.

If several runs in a row go wrong, you get an alert for the first three, and no more until a run goes well. When that happens, you get a notice that the job works again. So that a job that keeps failing and working doesn't flood you, each job sends at most 20 notices in 24 hours.

If a run starts while another is still running, the history shows it. For a job that must not run twice at once, check the option in its form to also get an alert, at most 3 in 24 hours. It needs the call for the start.

Set up from an AI agent (MCP)

CronDrive has an MCP server, so an agent like Claude Code, Cursor or Codex can set up the monitoring for you: it reads the server's crontab, creates a job for each line and adds the calls to CronDrive.

  1. Create a token in Settings.
  2. Add the server to your agent. In Claude Code:

    claude mcp add --transport http --scope user crondrive https://crondrive.com/mcp --header "Authorization: Bearer <token>"

    Other agents need the server's URL, https://crondrive.com/mcp, and the header Authorization: Bearer <token>.

  3. Ask it, on the server: “Monitor the cron jobs of this server with CronDrive.”

The agent creates the jobs paused, shows you the new crontab lines and resumes the jobs once the lines are in place. Later you can ask it about your jobs, like why a job failed last night.

Tool Description
list_jobs Your jobs, with their latest and next run
get_job A job, with its crontab line
create_job Creates a job, paused, and returns its crontab line
update_job Changes a job's name, schedule, time zone or alerts
pause_job Pauses the monitoring of a job
resume_job Resumes the monitoring of a job
list_runs A job's runs, or only those that went wrong

Jobs can only be deleted here, so an agent can't lose their history by mistake. A token gives access to all your jobs; revoke it in Settings when you no longer use it.

Integration with Slack

If your team uses Slack, CronDrive can send the alerts to a Slack channel.

Connect it in Settings and choose the channel for the alerts.

Webhooks

CronDrive can also send the alerts to Discord, Microsoft Teams, Google Chat, Telegram, ntfy, PagerDuty, services that take Slack's format, or a URL of yours. Add them in Settings.

Each service comes with its template, so you only give the URL or the key it asks for. You can also edit the request (method, URL, headers and body) with these variables, which are escaped for where they go:

{{job.name}}
The job's name
{{job.schedule}}
The cron expression
{{job.time_zone}}
The job's time zone
{{job.url}}
The job's history in CronDrive
{{event}}
alert or recovered
{{status}}
start, end, failed, overlap or recovered
{{exit_code}}
The exit code; empty if the job didn't finish
{{output}}
The last 10 kB of what the failed command wrote, if your plan saves it; empty otherwise
{{due_at}}
When the run was due (ISO 8601)
{{message}}
The text of the alert, in your language
{{dedup_key}}
The same in the alert and the recovery; another one for overlaps

Besides the alerts, a webhook can be told when a job works again after an alert, with the same {{dedup_key}}: PagerDuty uses it to resolve the incident the alert opened. An overlap alert has its own {{dedup_key}}, since no recovery closes it.

Webhooks only go to public https addresses. If 3 alerts in a row can't be delivered, the webhook is paused and you get an email.

API reference

Description Endpoint
Report that the job started GET https://job.crondrive.com/{token}/start
Report that the job started, with an id of the run GET https://job.crondrive.com/{token}/start/{run id}
Report that the job ended, with its exit code; one other than 0 alerts GET https://job.crondrive.com/{token}/end/{exit code}
Report that the job ended, with its exit code and the id of the run its start sent (up to 64 letters, digits, dots, dashes or underscores) GET https://job.crondrive.com/{token}/end/{exit code}/{run id}
Report that the job ended, without an exit code GET https://job.crondrive.com/{token}/end
Any of the end calls, with the command's output in the body; the last 10 kB are kept, with the Pro plan POST https://job.crondrive.com/{token}/end/{exit code}/{run id}
Pause the monitoring; no alerts are sent GET https://job.crondrive.com/{token}/pause
Resume the monitoring GET https://job.crondrive.com/{token}/resume

Both calls can end with an id of the run, https://job.crondrive.com/{token}/start/{run id} and https://job.crondrive.com/{token}/end/{exit code}/{run id}, so CronDrive pairs each end with its start even when a run lasts longer than the time between runs. The crontab line uses $$, which is the same in both calls of the line.

Every call answers 200 with an empty body, also for an unknown token. Each token accepts one call per second.