How to Set Up Cron Jobs in Ruby on Rails
Learn how to schedule cron jobs in Ruby on Rails using the whenever gem, sidekiq-cron, and custom rake tasks. Step-by-step guide with deployment tips.
Prerequisites
- Ruby 3.1+
- Rails 7+
- Bundler
In this guide
Cron Options in Rails
Rails offers several approaches for scheduled tasks:
- whenever gem — DSL that generates crontab entries from Ruby code
- sidekiq-cron — Cron scheduling on top of Sidekiq (requires Redis)
- solid_queue — Rails 8+ built-in job backend with recurring tasks
- Custom rake tasks + system cron — Simple, no dependencies
For most Rails apps, the whenever gem is the easiest starting point. For apps already using Sidekiq, sidekiq-cron integrates naturally.
Quick Start with the whenever Gem
Install:
# Gemfile
gem 'whenever', require: falsebundle install
wheneverize .This creates config/schedule.rb.
Define your schedule:
# config/schedule.rb
every 5.minutes do
runner "CleanupService.run"
end
every 1.day, at: '4:30 am' do
rake "reports:daily"
end
every :monday, at: '9:00 am' do
runner "WeeklyDigest.send_all"
endApply to crontab:
whenever --update-crontab
whenever -l # List generated crontab entriesSidekiq-Cron for Background Jobs
If you're already using Sidekiq, sidekiq-cron adds recurring job support:
# Gemfile
gem 'sidekiq-cron'Define recurring jobs (config/initializers/sidekiq_cron.rb):
Sidekiq::Cron::Job.load_from_hash(
'cleanup' => {
'cron' => '*/5 * * * *',
'class' => 'CleanupWorker',
},
'daily_report' => {
'cron' => '0 9 * * *',
'class' => 'DailyReportWorker',
}
)# app/workers/cleanup_worker.rb
class CleanupWorker
include Sidekiq::Worker
def perform
OldRecord.where('created_at < ?', 30.days.ago).delete_all
end
endSidekiq-cron provides a web UI at /sidekiq/cron to monitor jobs.
Custom Rake Tasks
The simplest approach — write a rake task and schedule it via system cron:
# lib/tasks/maintenance.rake
namespace :maintenance do
desc "Clean up expired sessions"
task cleanup: :environment do
deleted = Session.expired.delete_all
puts "Deleted #{deleted} expired sessions"
end
end# Add to crontab
crontab -e
0 2 * * * cd /opt/myapp && RAILS_ENV=production bundle exec rake maintenance:cleanup >> /var/log/cron.log 2>&1Always set RAILS_ENV=production and use bundle exec to ensure the correct Ruby and gems are used.
Common Cron Expressions
Standard 5-field expressions used with system cron or sidekiq-cron:
*/5 * * * *— Every 5 minutes0 * * * *— Every hour0 0 * * *— Daily at midnight0 9 * * 1-5— Weekdays at 9 AM0 0 1 * *— First day of every month
With the whenever gem, use Ruby syntax instead:
every 5.minutes { runner "Task.run" }
every 1.hour { rake "process:queue" }
every 1.day, at: '12:00 am' { runner "Cleanup.run" }
every :weekday, at: '9:00 am' { runner "Report.send" }Every weekday at 9:00 AM
Next runs (UTC):
Mon, May 18, 2026 09:00
Tue, May 19, 2026 09:00
Wed, May 20, 2026 09:00
Deployment with Capistrano
The whenever gem integrates with Capistrano:
# Gemfile
gem 'whenever', require: false# config/deploy.rb
set :whenever_roles, -> { :app }
# This automatically updates crontab on deploy
require 'whenever/capistrano'For Docker deployments, you'll need to run the cron daemon in your container or use a sidecar approach. See our Docker Cron Setup Guide for details.
Debugging
Task not running?
- Check crontab is installed:
crontab -l - Verify the Ruby/Rails environment: set
RAILS_ENVand use full paths - Check cron logs:
grep CRON /var/log/syslog
whenever preview:
whenever # Shows the crontab entries that would be generated (dry run)sidekiq-cron debugging:
# Rails console
Sidekiq::Cron::Job.all.each do |job|
puts "#{job.name}: #{job.cron} - #{job.status}"
endProduction Tips
Logging: Always log to a file and use Rails logger:
# In your scheduled task
Rails.logger.info "Starting daily cleanup"Error tracking: Wrap tasks with error reporting:
task cleanup: :environment do
Cleanup.run
rescue => e
Sentry.capture_exception(e)
raise
endHealth checks: Ping a monitoring service after successful runs:
every 1.hour do
runner "ProcessQueue.run; Net::HTTP.get(URI('https://hc-ping.com/uuid'))"
end