CronPup

← Guides

Monitoring Rails scheduled jobs (whenever, sidekiq-cron, Solid Queue)

Rails apps schedule recurring work in two ways: system cron (often generated by the whenever gem), or a job backend's recurring scheduler (sidekiq-cron, sidekiq-scheduler, Solid Queue recurring tasks). Both fail silently in the same ways. The crontab or scheduler process goes missing after a deploy, the job raises and only the logs know, or the job hangs.

Jobs scheduled with whenever (system cron)

Wrap the command with cronpup-run. It reports start, success or failure, the exit code and the last lines of output:

# config/schedule.rb
every 1.day, at: "3:00 am" do
  command "cronpup-run https://cronpup.com/p/REPORT-UUID bash -lc 'cd /app && bin/rails runner Reports.nightly'"
end

Set the CronPup monitor's schedule to 0 3 * * * in the server's timezone.

Jobs run by Sidekiq, Solid Queue or another queue

Ping from inside the job, so you know the job actually ran, not just that it was enqueued:

# lib/heartbeat.rb
require "net/http"

module Heartbeat
  def self.ping(url)
    uri = URI(url)
    Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https",
                    open_timeout: 5, read_timeout: 5) { |http| http.get(uri.request_uri) }
  rescue StandardError => e
    Rails.logger.warn("heartbeat ping failed: #{e.message}")  # never let monitoring break the job
  end
end

# app/jobs/nightly_report_job.rb
class NightlyReportJob < ApplicationJob
  def perform
    url = ENV.fetch("NIGHTLY_REPORT_PING_URL")
    Heartbeat.ping("#{url}/start")
    Reports.nightly
    Heartbeat.ping(url)
  rescue StandardError
    Heartbeat.ping("#{url}/fail") if url
    raise
  end
end

Re-raising keeps the queue's normal retry behaviour. If a retry later succeeds, its success ping marks the monitor up again, and you get a recovery notice.

Pick the schedule and grace period

Keep ping URLs in environment variables, so staging doesn't ping production monitors.

Monitor your Rails jobs. 10 monitors are free.