Cron Jobs

Windows Scheduled Task Monitoring

Windows Scheduled Tasks are jobs Task Scheduler runs on a schedule. Windows Services are long-running processes the OS keeps alive. Cronitor monitors both, but they are not the same thing and the setup is different.

  • Scheduled Tasks — install CronitorCLI and run cronitor sync. It scans Task Scheduler, creates job monitors, and wraps the tasks you choose.
  • Windows Services — same CLI install, then a Heartbeat monitor. Do not wrap a Service with cronitor exec.

If you meant a scheduled job, start with Scheduled Tasks. If the process is a Service, skip to Windows Services.

Monitor Windows Scheduled Tasks

CronitorCLI is the supported path. It creates job monitors from Task Scheduler and rewrites each selected Exec action's Program/script to C:\Program Files\cronitor.exe with arguments exec <key> <original command>. Creating a monitor by hand and wrapping the action yourself is a fallback, not the default.

You will get alerts when a task does not start on schedule, fails, or runs longer than expected.

1. Install CronitorCLI

Run PowerShell as Administrator:

Invoke-WebRequest -Uri "https://cronitor.io/dl/windows_amd64.zip" -OutFile "$env:TEMP\cronitor.zip"
Expand-Archive -Path "$env:TEMP\cronitor.zip" -DestinationPath "$env:ProgramFiles\Cronitor" -Force
Remove-Item "$env:TEMP\cronitor.zip"
[Environment]::SetEnvironmentVariable("Path", "$env:Path;$env:ProgramFiles\Cronitor", "Machine")

This installs cronitor.exe into C:\Program Files\Cronitor and adds that folder to the machine PATH. Update later with cronitor update.

That is not the path cronitor sync writes. The published Windows binary still rewrites Program/script to C:\Program Files\cronitor.exe. Until a CLI fix lands, prefer a symlink so cronitor update does not leave a stale copy:

New-Item -ItemType SymbolicLink -Path "$env:ProgramFiles\cronitor.exe" -Target "$env:ProgramFiles\Cronitor\cronitor.exe"

Run cronitor update from the PATH location (C:\Program Files\Cronitor\cronitor.exe), not through the C:\Program Files\cronitor.exe symlink — the updater rewrites whatever path it was launched from, so updating through the symlink would replace it with a real file and reintroduce the stale copy.

If you cannot create a symlink, copy instead, and re-copy after every cronitor update:

Copy-Item "$env:ProgramFiles\Cronitor\cronitor.exe" "$env:ProgramFiles\cronitor.exe"

Or, after sync, edit each rewritten action so Program/script is C:\Program Files\Cronitor\cronitor.exe.

2. Configure the CLI

Open a new elevated terminal so the updated PATH takes effect (the config file is written under ProgramData), then:

cronitor configure --api-key 'YOUR_API_KEY' --api-version '2025-11-28'

Replace YOUR_API_KEY with the key from API settings. Do not commit the key or paste it into tickets or screenshots. Without --api-version '2025-11-28', Windows sync creates monitors with no schedule.

3. Sync Task Scheduler

Preview first with cronitor sync --dry-run — it creates monitors but does not rewrite Task Scheduler. Then run cronitor sync as Administrator. It scans Task Scheduler, lets you choose which tasks to monitor, creates job monitors, and rewrites each selected Exec action.

> cronitor sync
----► Scanning for scheduled tasks...
 |--► Found 15 scheduled tasks
 |--► Task: Daily Reports

   What would you like to do with this task?
 │ ✔ Monitor this task
 │   Monitor this task and Rename
 │   Skip this task

----► Sync complete ✔

For each task you monitor, CronitorCLI:

  • Creates a job monitor whose schedule comes from the Task Scheduler triggers (as RRULEs)
  • Rewrites TASK_ACTION_EXEC actions to Program/script C:\Program Files\cronitor.exe and arguments exec <key> <original command>

What cronitor sync skips

  • Built-in tasks under \Microsoft\...
  • Tasks that only have disabled, one-time, or session-state-change triggers
  • Non-Exec actions (COM, email, message). Only TASK_ACTION_EXEC actions are wrapped.

Boot- and logon-triggered tasks are included, but they get a note on the monitor instead of a schedule. Cronitor can still track success and failure, not a missed start.

Updating a task requires Administrator (or equivalent permission). If a wrap fails, the CLI warns you. The monitor is still created — wrap that action by hand using the fallback below.

cronitor sync --auto is not a preview. On Windows it takes every non-Microsoft task with an eligible trigger and both creates a monitor and rewrites the action, with no prompt and no selection — different from the interactive flow above. The safe first pass is cronitor sync --auto --dry-run (creates or updates monitors, does not rewrite Task Scheduler on Windows). Review that list, then run cronitor sync --auto only if you want every eligible task wrapped.

--notification-list <list> sets the alert list when monitors are created. Pass it on every cronitor sync — a re-run without it resets alerts to default. cronitor sync --no-stdout does nothing on Windows. If a task prints secrets, add --no-stdout to the action yourself, before the monitor key: exec --no-stdout YOUR_MONITOR_KEY ....

4. Verify

Run a monitored task from Task Scheduler. You should see the run on your Cronitor dashboard within a few seconds. If the task fails with "file not found", the wrap path does not match the install — symlink or copy the exe, or edit the action, as in step 1.

5. Re-run sync when tasks change

Re-run cronitor sync when you add, rename, or change scheduled tasks. Use it to rename monitors or pick up new tasks.

Fallback: wrap an action yourself

If you cannot run cronitor sync, or it could not update a task, wrap the action by hand. Create a job monitor in the dashboard, copy its key, then edit the task:

  1. Right-click the task, select Properties, select Actions, select the action, click Edit.

  2. Turn the command into an argument to cronitor exec:

    Program/scriptArguments
    BeforeC:\app\bin\collect-statsPRODUCTION
    AfterC:\Program Files\Cronitor\cronitor.exeexec YOUR_MONITOR_KEY C:\app\bin\collect-stats PRODUCTION
  3. If the task has multiple actions, repeat this for each one.

  4. If the command prints secrets, add --no-stdout before the monitor key: exec --no-stdout YOUR_MONITOR_KEY C:\app\bin\collect-stats PRODUCTION.

If you cannot install CronitorCLI at all, this wrap is not available. On a locked-down host, move the command into a .bat or .ps1 and ping /run, then /complete or /fail from the exit code, with curl or Invoke-RestMethod. Use a telemetry-only key with monitor:telemetry permission, not the management key from cronitor configure. See the Telemetry API. Do not use this as the default path.

Invoke-RestMethod "https://cronitor.link/p/YOUR_TELEMETRY_API_KEY/YOUR_MONITOR_KEY?state=run"
& C:\app\bin\collect-stats PRODUCTION
$state = if ($LASTEXITCODE -eq 0) { 'complete' } else { 'fail' }
Invoke-RestMethod "https://cronitor.link/p/YOUR_TELEMETRY_API_KEY/YOUR_MONITOR_KEY?state=$state&status_code=$LASTEXITCODE"

Monitor Windows Services

A Windows Service is a long-running process the OS keeps alive. It is not a Task Scheduler job. Cronitor does not wrap a Service with cronitor exec, and there is no separate agent that attaches to Services.

Use the same CLI install as above, then send heartbeats.

  1. Create a Heartbeat monitor. Set how often you expect a ping — every 1 minute or every 5 minutes is typical.
  2. Save this script (for example as C:\Scripts\cronitor-service-heartbeat.ps1), replacing the service name and monitor key:
$svc = Get-Service 'YourServiceName' -ErrorAction SilentlyContinue
if ($svc -and $svc.Status -eq 'Running') {
  & 'C:\Program Files\Cronitor\cronitor.exe' ping YOUR_MONITOR_KEY --tick
} else {
  $state = if ($svc) { $svc.Status } else { 'not installed' }
  & 'C:\Program Files\Cronitor\cronitor.exe' ping YOUR_MONITOR_KEY --fail --msg "YourServiceName is $state"
}
  1. Create a Task Scheduler job that runs it every minute. In the Actions dialog:

    Program/scriptArguments
    powershell.exe-NoProfile -ExecutionPolicy Bypass -File C:\Scripts\cronitor-service-heartbeat.ps1

    Or from an elevated prompt:

schtasks /Create /TN "Cronitor heartbeat - YourServiceName" /RU SYSTEM /SC MINUTE /MO 1 /TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\cronitor-service-heartbeat.ps1"

Use --tick for a healthy heartbeat and --fail when the Service is not running. The current CLI requires a state flag: --run, --complete, --fail, --ok, or --tick. Do not use --state fail, and do not call cronitor ping KEY with no flag. If a failure rule stays red after ticks resume, cronitor ping YOUR_MONITOR_KEY --ok resets it.

If the Service already listens on HTTP, a Check monitor against that URL is simpler and needs no CLI.

If you meant a scheduled job rather than a Service, use the Task Scheduler path above.

Resources

Previous
Java Cron Jobs