Sidekiq Job Monitoring with DeadManCheck

Sidekiq tells you when a job raises an exception. It doesn't tell you when a job runs, processes zero records, and exits cleanly. DeadManCheck does.

What Sidekiq's built-in monitoring misses

Sidekiq's retry mechanism and dead queue handle raised exceptions well. But the failures that go unnoticed aren't exceptions — they're silent successes: jobs that complete with no error and no useful output.

The dead man's switch pattern fixes this: your job pings an external service on success, and that service alerts you if it stops hearing from the job on schedule.

Add monitoring to any Sidekiq worker

Add start, success, and fail pings to your worker's perform method. Uses Ruby's stdlib Net::HTTP — no extra gem required.

# app/workers/daily_export_worker.rb
require 'net/http'
require 'json'

class DailyExportWorker
  include Sidekiq::Job

  TOKEN = ENV['DEADMANCHECK_TOKEN']
  BASE = "https://deadmancheck.io/ping/#{TOKEN}"

  def perform
    dmc_start

    rows = run_export

    dmc_success(rows)
  rescue
    dmc_fail
    raise # re-raise so Sidekiq handles retries
  end

  private

  def dmc_start
    Net::HTTP.get(URI("#{BASE}/start"))
  rescue; end

  def dmc_success(count)
    uri = URI(BASE)
    req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
    req.body = { count: count }.to_json
    Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }
  rescue; end

  def dmc_fail
    Net::HTTP.get(URI("#{BASE}/fail"))
  rescue; end
end

Set the monitor interval slightly longer than your schedule — e.g. 25 hours for a daily job. DeadManCheck alerts if the success ping doesn't arrive within that window.

Using sidekiq-cron or sidekiq-scheduler?

The same worker pattern applies. If you're using sidekiq-cron or sidekiq-scheduler to run jobs on a schedule, your perform method is already the right place to add the pings — no extra configuration needed.

# config/schedule.yml (sidekiq-scheduler)
daily_export:
  cron: "0 2 * * *"
  class: DailyExportWorker
  queue: default

# The worker above handles all pings — nothing else to configure.

One monitor per scheduled job. If sidekiq-cron loses its Redis state after a restart, DeadManCheck will alert you — the expected ping never arrives.

Output assertions: catch workers that do nothing

DeadManCheck's output assertion feature alerts you when a job completes but the count it reports is below a threshold you set. Cronitor, Healthchecks.io, and Better Stack don't have this — they only check that the ping arrived.

Pass the count in the POST body and configure your threshold in the monitor settings:

# POST to /ping/{token} with count — triggers assertion check
uri = URI("https://deadmancheck.io/ping/#{TOKEN}")
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
req.body = { count: rows_exported }.to_json
Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |h| h.request(req) }

See how output assertions work →

Duration monitoring for slow workers

DeadManCheck tracks how long each job run takes and alerts when a run takes significantly longer than the rolling average. The start ping triggers the timer; the success ping stops it.

After 5+ runs, DeadManCheck builds a rolling baseline. A job that normally takes 30 seconds but suddenly takes 8 minutes gets flagged — a leading indicator of a slow query, a data volume spike, or a downstream service degrading.

Learn more about duration monitoring with rolling average anomaly detection →

Setup in 2 minutes

  1. Create a free monitor — set interval to match your job schedule + buffer
  2. Copy your monitor token into DEADMANCHECK_TOKEN env var
  3. Add the dmc_start / dmc_success / dmc_fail helpers to your worker
  4. Optionally configure an output assertion threshold in the monitor settings
  5. Deploy and watch the first ping arrive in the dashboard

Related guides

Start monitoring free — no credit card needed

Free for 5 monitors. $12/mo for 100. Self-host for free.