cronuru
Guide

Ruby Cron Scheduling — A Complete Guide

Ruby scheduling falls into three camps: write to the system crontab (`whenever`), schedule inside a job queue (`sidekiq-cron`, `delayed_job`), or run a dedicated scheduler process (`clockwork`, `rufus-scheduler`). The right pick depends on whether you're on Heroku, what queue you already use, and how much restart resilience you need.

Updated

The landscape

GemWhat it doesBest for
wheneverRuby DSL that writes to the system crontabSelf-hosted servers where you control crontab
sidekiq-cronCron scheduling for Sidekiq job queueRails apps already using Sidekiq
clockworkStandalone Ruby scheduler processApps without queue infrastructure
rufus-schedulerIn-process scheduler libraryLong-running Ruby services
resque-schedulerCron scheduling for Resque queueApps using Resque
delayed_jobActiveRecord-backed job queue with run_atSimple Rails background jobs
good_jobPostgreSQL-backed job queue with cronRails apps wanting no extra infra
solid_queueRails 8 default queue (with cron support)New Rails apps
Heroku SchedulerHeroku add-on for scheduled dynosHeroku-hosted apps with simple needs

The biggest split: system-cron-based (whenever, Heroku Scheduler) vs in-app-scheduler (sidekiq-cron, clockwork, rufus, etc.). System-cron requires shell access on the deploy host; in-app schedulers run inside your Ruby process(es).

whenever — Ruby DSL → crontab

The classic Rails scheduling gem. Writes a Ruby DSL into the host’s crontab.

bundle add whenever
wheneverize .

config/schedule.rb:

set :output, "log/cron.log"
set :environment, "production"

every 5.minutes do
  rake "cache:refresh"
end

every :hour do
  rake "stats:rollup"
end

every :day, at: "9:00 am" do
  runner "DigestMailer.daily.deliver_now"
end

every :weekday, at: "9:00 am" do
  command "/usr/local/bin/check-disk-space.sh"
end

every "*/15 * * * *" do
  rake "polling:check"
end

After deploy, run:

bundle exec whenever --update-crontab myapp

This writes entries into the user’s crontab. The Ruby DSL compiles to standard cron expressions — every 5.minutes becomes */5 * * * *, every :day, at: "9:00 am" becomes 0 9 * * *, etc.

What whenever is good for:

  • Self-hosted servers where you have shell access
  • Rails apps deployed via Capistrano (whenever has built-in Capistrano hooks)
  • Teams who prefer a Ruby DSL over editing crontab directly

What it isn’t good for:

  • Heroku or other platforms that don’t expose user crontabs
  • Multi-instance deploys (the crontab is per-host; if you have N app servers, each runs the job N times)
  • Jobs that need restart-aware retry — system cron has no retry built in

sidekiq-cron

Adds cron scheduling to Sidekiq. Schedule is stored in Redis; Sidekiq workers pick up scheduled jobs and run them.

bundle add sidekiq-cron

Define jobs as regular Sidekiq workers:

class DailyDigestJob
  include Sidekiq::Job

  def perform
    User.subscribed.find_each(&:send_digest)
  end
end

Register the schedule (e.g., in config/initializers/sidekiq.rb):

schedule = {
  "daily-digest" => {
    "class" => "DailyDigestJob",
    "cron"  => "0 9 * * MON-FRI",
    "queue" => "default",
    "args"  => [],
  },
  "cache-refresh" => {
    "class" => "CacheRefreshJob",
    "cron"  => "*/5 * * * *",
  },
}

Sidekiq::Cron::Job.load_from_hash(schedule)

Or from a YAML file:

# config/schedule.yml
daily-digest:
  class: DailyDigestJob
  cron: "0 9 * * MON-FRI"

cache-refresh:
  class: CacheRefreshJob
  cron: "*/5 * * * *"
# initializer
Sidekiq::Cron::Job.load_from_hash YAML.load_file("config/schedule.yml")

What sidekiq-cron does that whenever doesn’t:

  • Single-fire across many workers. sidekiq-cron coordinates via Redis — even with 10 worker dynos, a */5 * * * * schedule fires exactly once every 5 minutes.
  • Sidekiq features apply. Retries, dead-letter queue, monitoring via the Sidekiq web UI — all the things Sidekiq gives you for ad-hoc jobs apply to scheduled jobs.
  • Restart-resilient. Schedule lives in Redis, not in any single process.

For Rails apps already using Sidekiq, this is the default answer. If you’re not using Sidekiq yet, consider whether you need a job queue at all — for simple scheduling, whenever or Heroku Scheduler may be lighter.

clockwork

A standalone Ruby scheduler that runs in its own process and dispatches work to wherever you need it.

bundle add clockwork

clock.rb:

require "clockwork"
require_relative "config/environment"

module Clockwork
  every(5.minutes, "cache.refresh") { CacheRefreshJob.perform_later }
  every(1.hour,    "stats.rollup")  { StatsJob.perform_later }
  every(1.day, "digest", at: "9:00") { DigestJob.perform_later }
end

Run with:

bundle exec clockwork clock.rb

clockwork itself doesn’t run the work — it dispatches to ActiveJob (perform_later), which in turn enqueues to your configured backend (Sidekiq, Solid Queue, etc.). This separation is the appeal: clockwork is “just” a scheduler, the queue is your existing queue.

Heroku users often pair clockwork with worker dynos: one clock process runs clockwork, N worker processes run the queue. clockwork was originally created to solve Heroku-doesn’t-have-crontab without paying for the Scheduler add-on.

The downside: single point of failure. If the clock dyno crashes, scheduling stops. Heroku auto-restarts dynos but there’s a brief gap.

For Rails 7+ apps, sidekiq-cron or solid_queue’s recurring jobs are usually a better fit — they coordinate via the database/Redis so the scheduling itself is highly available.

Rufus::Scheduler

A general-purpose in-process Ruby scheduler. Use it in any long-running Ruby code.

bundle add rufus-scheduler
require "rufus-scheduler"

scheduler = Rufus::Scheduler.new

scheduler.cron "*/5 * * * *" do
  # runs every 5 minutes
end

scheduler.every "10s" do
  # runs every 10 seconds (sub-minute precision)
end

scheduler.at "2026-12-31 23:59:00" do
  # one-off
end

# Keep the process alive
scheduler.join

Rufus is the scheduling engine behind resque-scheduler and sidekiq-scheduler. (sidekiq-cron is a different gem — it parses its cron lines with Fugit, the cron-parsing library that rufus itself depends on, rather than running on rufus.) For non-Rails Ruby services (Sinatra apps, daemons, CLI tools), rufus-scheduler is the simplest way to add scheduling.

Caveats:

  • In-process — process restart loses any in-memory state (use the Sequel persistence option for restart-resilient triggers)
  • Multi-instance — running rufus in two processes fires every job twice
  • Time precision — Rufus has sub-second precision, but real-world drift means it’s not suitable for “exactly at this microsecond” needs

Heroku Scheduler

The managed scheduling add-on for Heroku. No code, no gems — configure schedules in the Heroku dashboard.

heroku addons:create scheduler:standard
heroku addons:open scheduler

In the web UI, add jobs like:

Schedule: Every 10 minutes
Command: rake digest:check

Each scheduled fire starts a fresh one-off dyno, runs the command, and shuts down. Limits:

  • Intervals: 10 minutes, hourly, or daily only. No 5-minute, 30-minute, or 2-hour options.
  • Daily jobs fire at a specific UTC time. No per-job timezone.
  • Best-effort timing. Heroku doesn’t guarantee exact firing times — jobs are expected to run but can occasionally be skipped or run twice.
  • Cold dyno startup counts against your hour-budget. Each run spins up a one-off dyno, and that startup time is billed along with the job itself — factor it in for short, frequent jobs.

For Heroku apps with simple needs, Scheduler is the path of least resistance. For anything that needs 5-minute or sub-minute granularity, custom intervals, or per-job timezone — pair Sidekiq + sidekiq-cron on a worker dyno instead.

Choosing the right one

  • Rails app with Sidekiq already?sidekiq-cron
  • Rails app, no queue yet, on a self-hosted server?whenever (generates crontab) or solid_queue (Rails 8 default with recurring jobs)
  • Rails app on Heroku, simple schedule? → Heroku Scheduler add-on
  • Rails app on Heroku, needs 5-minute granularity or per-job timezone?sidekiq-cron + Redis + worker dyno
  • Non-Rails Ruby service?rufus-scheduler (in-process) or invoke from system cron
  • Rails app + ActiveJob, want to keep things in-DB?good_job (PostgreSQL) with its built-in cron, or solid_queue recurring jobs
  • Need SLA-grade reliability? → Move scheduling out of Ruby — use Kubernetes CronJob, AWS EventBridge Scheduler, or system cron to invoke a Ruby HTTP endpoint or rake task on a schedule.

For most production Rails apps in 2026, the answer is sidekiq-cron (with Sidekiq) or solid_queue recurring jobs (the Rails 8 default; works on PostgreSQL, MySQL, or SQLite — no Redis needed). Both coordinate properly across multiple dynos, persist the schedule, and integrate with the rest of your queue infrastructure.

Frequently asked questions

Which Ruby scheduling gem should I use?
If you're already using Sidekiq: sidekiq-cron. If you control the deploy host's crontab: whenever (generates crontab entries from a Ruby DSL). If you're on Heroku or another platform that doesn't expose crontab: Heroku Scheduler add-on, or move to a Sidekiq-style queue. For long-running Ruby processes with in-memory scheduling: clockwork or rufus-scheduler.
Does whenever actually run my jobs?
No. `whenever` generates entries for the system crontab — actual job execution is handled by cron. When you deploy, whenever's `whenever --update-crontab` writes the new schedule to the user's crontab. Cron then runs each scheduled command at the right time. Don't use whenever on platforms without system cron access (Heroku, most managed PaaS).
Is sidekiq-cron officially part of Sidekiq?
No. sidekiq-cron is a third-party gem that adds cron scheduling to Sidekiq. Sidekiq itself has 'enterprise periodic jobs' as a paid feature. sidekiq-cron is the free alternative — Redis-backed, stores the schedule in Redis, integrates with the Sidekiq web UI.
How does Heroku Scheduler compare to system cron?
Heroku Scheduler is a managed add-on that runs a one-off dyno on a schedule. It's simpler than installing whenever/clockwork but has limitations: only 10-minute, hourly, or daily intervals (no 5-minute or 2-hour options), no per-second precision, and cold dyno startup eats into your useful runtime. For anything more demanding, use sidekiq-cron with a worker dyno.
What's the difference between clockwork and Rufus::Scheduler?
Both are in-process Ruby schedulers. clockwork is built around the pattern of running its own process (alongside your Rails app) that dispatches work to a queue. Rufus::Scheduler is more general — it's just a scheduler library you embed anywhere. For Rails apps, clockwork's queue-dispatch pattern is usually cleaner. For non-Rails Ruby code, Rufus is more flexible.