Rails Cron Jobs with Whenever
Rails has no built-in scheduler — the de facto standard is the whenever gem, which lets you define jobs in a readable Ruby DSL (config/schedule.rb) and generates an actual crontab from it, rather than requiring you to hand-write cron syntax directly.
Syntax Notes
Whenever's DSL uses human-readable frequency methods (every 1.day, at: '2:30 am', every :sunday) rather than raw cron fields — it compiles schedule.rb down to a real crontab behind the scenes, viewable by running `whenever` with no arguments before writing it.
Examples
every 1.day, at: '2:30 am' do
runner "MyModel.cleanup"
endevery 15.minutes do
rake "my_task:run"
endwhenever --update-crontabGotchas
- Editing config/schedule.rb does nothing to the actual crontab by itself — you must run `whenever --update-crontab` (typically as a deploy step, e.g. via Capistrano's whenever integration) to write the compiled schedule into cron.
- Whenever's generated crontab jobs run outside your application's Bundler/RVM/rbenv context by default — the gem's environment-setting options (set :output, set :environment, and the job_type templates) exist specifically to work around cron's minimal shell environment, and skipping them is the most common cause of 'works locally, silently fails via cron' bugs.
- Deploying without re-running `whenever --update-crontab` leaves the old schedule active — schedule.rb changes have zero effect on a running server until the crontab is regenerated and rewritten.
Best Practices
- Wire `whenever --update-crontab` into your deploy pipeline automatically (Capistrano has an official whenever plugin) rather than remembering to run it by hand after every schedule.rb change.
- Always redirect output in schedule.rb jobs (whenever's set :output config, or add >> log/cron.log 2>&1 to a custom job_type) so failures are visible instead of silently swallowed by cron's minimal environment.
- Run `whenever` with no arguments locally to preview the exact crontab that will be generated before pushing it to a server — it's the fastest way to catch a DSL mistake before it reaches production.
Frequently Asked Questions
- Do I need to know cron syntax to use the whenever gem?
- Not really — whenever's DSL (every 1.day, at: '2:30 am', every :monday) is designed to be readable without cron knowledge, and it compiles down to a real crontab for you. Understanding cron helps when debugging the generated output, though.
- Why isn't my updated schedule.rb taking effect on the server?
- Editing schedule.rb only changes a Ruby file — it has no effect on the actual system crontab until you run `whenever --update-crontab`, which regenerates and rewrites the real cron entries. This step is often missing from manual, non-Capistrano deploy processes.
- Why does my whenever-scheduled job fail via cron but work when I run it manually?
- Cron jobs run outside your shell's Bundler/rbenv/RVM context by default. Make sure whenever's environment-setting options are configured (set :environment, and the appropriate job_type) so the generated crontab entries load the correct Ruby version and gemset.
Related Tools
Platform Guides
Cron syntax for Linux, Kubernetes, AWS, and more
Vercel Cron Jobs Guide
Schedule serverless functions on Vercel with Cron Jobs: vercel.json syntax, plan-based frequency limits, authentication, and common gotchas.
Supabase Cron (pg_cron) Guide
Schedule recurring database jobs in Supabase with the pg_cron extension: cron.schedule() syntax, calling Edge Functions, and common gotchas.