June 7, 2026 7 min read
How to monitor Sidekiq Cron jobs in Rails
Sidekiq and sidekiq-cron schedule your jobs — they don't tell you when jobs miss, hang, or silently process nothing. Here's how to add external monitoring to every scheduled Sidekiq job in a Rails app.
Sidekiq is the standard background job processor for Rails applications. sidekiq-cron extends it with scheduled jobs, giving you a clean way to define recurring tasks inside your application code rather than managing system crontabs.
But scheduling and monitoring are different problems. sidekiq-cron fires your jobs on schedule. It does not tell you when a job fails to enqueue because the Sidekiq process went down, when a job starts but never completes, or when a job runs successfully but produces no useful output.
For those failure modes, you need external monitoring that watches your jobs from outside the Rails process.
What sidekiq-cron doesn't catch
sidekiq-cron runs a polling thread inside the Sidekiq process. It checks for due jobs every 10 seconds (configurable) and enqueues them into Redis. A Sidekiq worker then picks up and executes the job.
This architecture has three monitoring gaps:
Process failure. If the Sidekiq process crashes or is killed, sidekiq-cron's polling thread stops. No jobs are enqueued. No alerts fire. The jobs simply stop running until the process restarts.
Worker failure. If a job is enqueued but the worker throws an unhandled exception, Sidekiq retries it according to its retry policy. After exhausting retries, the job moves to the dead queue. Sidekiq Web shows this, but no external alert fires by default.
Silent failure. If a job runs to completion but processes zero records, sends no emails, or produces empty output, Sidekiq sees a successful job. No alert fires, because from Sidekiq's perspective, everything worked.
The monitoring pattern
External monitoring for Sidekiq Cron jobs uses the same dead man's switch model as any cron monitoring: your job sends HTTP pings when it starts and when it finishes. An external service tracks whether pings arrive on schedule.
Add a ping helper to your ApplicationJob or a dedicated concern:
# app/jobs/concerns/crontify_monitoring.rb
module CrontifyMonitoring
extend ActiveSupport::Concern
private
def ping(monitor_id, event, payload = {})
uri = URI("https://api.crontify.com/api/v1/ping/#{monitor_id}/#{event}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.open_timeout = 5
http.read_timeout = 5
request = Net::HTTP::Post.new(uri)
request["X-API-Key"] = ENV["CRONTIFY_API_KEY"]
request["Content-Type"] = "application/json"
request.body = payload.to_json unless payload.empty?
http.request(request)
rescue StandardError
# Monitoring failure must never kill the job
end
def with_crontify(monitor_id)
ping(monitor_id, "start")
meta = {}
yield meta
ping(monitor_id, "success", { meta: meta }.compact)
rescue => e
ping(monitor_id, "fail", {
message: e.message,
log: e.backtrace.first(20).join("\n")
})
raise
end
end
Include it in any scheduled job:
# app/jobs/nightly_sync_job.rb
class NightlySyncJob < ApplicationJob
include CrontifyMonitoring
MONITOR_ID = "your-monitor-id"
def perform
with_crontify(MONITOR_ID) do |meta|
result = SyncService.run
meta[:records_synced] = result.count
meta[:duration_ms] = result.duration_ms
meta[:errors] = result.errors
end
end
end
Schedule it in your sidekiq.yml or initializer as normal:
# config/sidekiq.yml
:schedule:
nightly_sync:
cron: "0 2 * * *"
class: NightlySyncJob
The with_crontify block sends a start ping before executing, a success ping with metadata when the block completes, and a fail ping with the error message and backtrace if an exception is raised.
Adding silent failure detection
The meta hash you populate inside the block is sent with the success ping and stored against the run record in Crontify. You then define alert rules in the dashboard:
records_synced eq 0→ fire alerterrors gt 0→ fire alertduration_ms gt 300000→ fire alert (job took more than 5 minutes)
A job that completes without error but syncs zero records will still trigger an alert. Without this, a silent failure in a Rails background job is invisible — Sidekiq marks it successful, the scheduler re-enqueues it next cycle, and the damage accumulates.
Handling jobs that run on multiple dynos or processes
If you run multiple Sidekiq processes (common on Heroku or Kubernetes), sidekiq-cron ensures only one process enqueues each scheduled job — it uses a Redis lock to prevent duplicates. This means you will still only get one start ping per scheduled execution, which is the correct behaviour.
If a job is enqueued multiple times due to a race condition (rare but possible), Crontify will detect the overlap and fire an alert. You can also add an around_perform callback using Redis locks to prevent concurrent execution entirely:
around_perform do |job, block|
lock_key = "job_lock:#{job.class.name}"
acquired = $redis.set(lock_key, "1", nx: true, ex: 3600)
if acquired
begin
block.call
ensure
$redis.del(lock_key)
end
else
Rails.logger.warn "#{job.class.name} already running, skipping"
end
end
What you get from external monitoring
After instrumenting your Sidekiq Cron jobs:
- Missed runs alert you when no start ping arrives within the grace period — catches Sidekiq process failures, Redis outages, and deployment interruptions that leave the scheduler stopped.
- Hung jobs alert you when a job starts but never completes — catches deadlocks and jobs stuck waiting on external services.
- Silent failures alert you when a job completes but its output metrics don't meet your defined thresholds.
- Recovery alerts fire automatically when a previously failing job returns to healthy.
Crontify is free for up to 5 monitors — no credit card required.
Start monitoring your scheduled jobs
Free plan includes 5 monitors. No credit card required. Up and running in under 5 minutes.
Get started free →More from the blog
June 7, 2026 6 min read
How to monitor GitHub Actions scheduled workflows
GitHub Actions scheduled workflows are unreliable by design — they can be delayed, skipped, or silently disabled. Here's how to know immediately when a scheduled workflow stops running.
Read more →
April 12, 2026 8 min read
How to monitor cron jobs in Python
Python's scheduler libraries don't tell you when jobs fail silently. Here's how to add missed run detection, hung job alerts, and silent failure detection to any Python cron job — in under five minutes.
Read more →