CronCanary DocsPricingOpen app
← Integration guides

Monitor sidekiq-cron scheduled jobs

sidekiq-cron schedules run inside the same Sidekiq process as your workers. If that process is down, restarting, or its Redis connection is broken, scheduled jobs simply never enqueue — with nothing job-side to report a failure.

Why sidekiq-cron jobs fail silently

Unlike a system crontab entry, sidekiq-cron doesn't run as a separate daemon — its polling loop lives inside the Sidekiq server process itself, checking Redis every few seconds for jobs whose next run time has passed. That design means the scheduler's own health is entirely dependent on Sidekiq's health: if the Sidekiq process is down after a deploy, crashed from a memory limit, or stuck because Redis is unreachable, cron jobs simply stop being enqueued — there is no separate "scheduler service" to alert on, and Sidekiq's own web UI will look empty and quiet rather than showing an error. A second, easy-to-miss cause is schedule drift: jobs are typically loaded once at boot via Sidekiq::Cron::Job.load_from_hash! from a YAML file, and if that file wasn't redeployed or a typo breaks the cron expression, the job silently disappears from the schedule with no runtime error — it just never appears in Sidekiq::Cron::Job.all again.

Ping from inside the job

Add the ping calls directly in perform, the same pattern as any monitored background job:

class NightlyReportJob include Sidekiq::Job URL = "$URL" # e.g. https://croncanary-ping.sleeezydesigns.workers.dev/<your-uuid> def perform Net::HTTP.get(URI("#{URL}/start")) rescue nil begin do_the_work Net::HTTP.get(URI(URL)) rescue nil # success rescue => e Net::HTTP.get(URI("#{URL}/fail")) rescue nil # failure -> alert raise e end end end

The schedule entry

Match the check's Cron schedule to the same expression you gave the job in config/schedule.yml:

nightly_report_job: cron: "0 4 * * *" class: "NightlyReportJob"

Load it the same way you already do at boot — nothing changes there:

Sidekiq.configure_server do |config| schedule_file = "config/schedule.yml" Sidekiq::Cron::Job.load_from_hash!(YAML.load_file(schedule_file)) if File.exist?(schedule_file) end

Catching Sidekiq itself being down

The per-job ping above only fires if the job actually runs — which is exactly the case that's missing when Sidekiq is down or Redis is unreachable. Because that's a whole-process failure, not a per-job one, there's nothing to "wrap" inside the job; the fix is the check itself. Set the check's Cron schedule and grace period tightly enough (a few minutes past the expected run time) that a dead Sidekiq process — which enqueues nothing — is caught by the missing ping alone, the same dead-man's-switch principle used for any process-dependent scheduler.


Related guides


Add a live status badge to your README

Every check has a public SVG badge that shows its live status (updates within ~1 minute). Paste this into any README — it doubles as a heartbeat anyone on the team can see:

[![CronCanary](https://croncanary.fluxath.app/badge/<your-check-id>.svg)](https://croncanary.fluxath.app)

Copy the exact markdown from your check's detail page. Add ?label=your-text to customize the left label.


Ready to wire this up? Create a free check — 20 checks, all alert channels, no card.