Cron Jobs
Scheduling Cron Jobs in Ruby
Ruby is one of the most beloved programming languages of the web era, so it's not surprising that it's used extensively for writing cron jobs.
In this article you’ll learn how to run any Ruby script as a cron job. We'll also explore some popular Ruby libraries that make it easier to manage cron jobs or replace them with tools like Sidekiq that avoid some of the pitfalls of traditional cron jobs.
What is a cron job?
Traditionally, cron jobs are user-defined scripts/commands that are executed on a repeating schedule by the Linux cron scheduler. However the term is now broadly used to mean a piece of code running in the background on a repeatable schedule.
What the * * * * * ?!?
One of the strangest parts of a cron job is the scheduling syntax, e.g. */3 2 * * 0-3.
Reminiscent of regular expressions, it is very hard to remember. See our guide on cron schedule syntax or use crontab.guru to translate a cron expression to english.
Run a Ruby script as a cron job
This script can be executed from the command prompt as:
ruby /path/to/script.rb
It's useful to manually test any new script before running it as a cron job. Even if you don't see any errors when running it directly, cron can still fail the same script later: the working directory, PATH, and environment variables are not your login shell. The troubleshooting guide walks through those cases. Absolute paths in the crontab avoid most of them.
Editing the Crontab
From the command line on your server, run crontab -e to access your crontab in a text editor. From there, you can paste or write in your crontab entry to run your Ruby script. To run the cron job at midnight every day, add the following line:
0 0 * * * ruby /path/to/script.rb
What the * * * * ?!?
The syntax for cron schedules is terse and tricky to remember. Our guide on Linux cron jobs covers cron syntax in detail.
Run cron jobs with the Whenever gem
While it's fairly easy to set up a simple example cron job, managing cron jobs can be tedious and error-prone. Writing and testing cron syntax can be difficult, especially for developers who are not familiar with the nuances of the Unix cron system. And trying to manage a large number of jobs without proper environments and version control can quickly turn to madness.
The whenever gem simplifies this process by allowing developers to define their cron jobs in Ruby code. Instead of writing complex cron syntax, developers can use a simple, readable DSL to define their jobs. Whenever then generates a proper crontab file and installs it on your server(s).
# execute a command
every 5.minutes do
command "/usr/bin/my_command"
end
Whenever also makes it easy to manage cron jobs across multiple servers using the concept of roles. Developers can define sets of jobs for different sets of servers by assigning those servers "roles" (e.g. app or db), and Whenever will generate separate cron files for each of those servers based on the assigned roles.
every :day, at: '1:37pm', roles: [:app] do
rake 'app:task' # will only be added to crontabs of :app servers
end
every :hour, roles: [:db] do
rake 'db:task' # will only be added to crontabs of :db servers
end
every :day, at: '12:02am' do
command "run_this_everywhere" # will be deployed to :db and :app servers
end
Run cron jobs with Sidekiq
Sidekiq is a popular library for running Ruby code in the background (asynchronously). Sidekiq enterprise, and the open-source sidekiq-scheduler gem both provide ways to run cron jobs via Sidekiq instead of relying on crontab.
This has some big advantages over traditional cron jobs. The queue-based architecture of Sidekiq means that important jobs can be prioritized, failed jobs retried, and long-running jobs are often easier to scale horizontally. Retries are part of Sidekiq. Rate limits are a Sidekiq Enterprise feature, not something the open-source gem does on its own. The setup below, and queue checks, are covered in monitoring Sidekiq.
Sidekiq Enterprise
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
# register any Jobs to be run on a cron based schedule
config.periodic do |mgr|
mgr.register "* * * * *", EveryMinuteJob, retry: 1
mgr.register "*/4 * 10 * *", OddlyTimedCriticalJob, queue: 'critical'
end
end
Monitor Ruby cron jobs
No matter which tool you use, cron jobs fail quietly if nobody is watching them. Cronitor alerts you when a job fails or doesn't run on time.
The Ruby SDK wraps a block. Sidekiq uses the sidekiq-cronitor gem (gem install sidekiq-cronitor, or gem 'sidekiq-cronitor' in the Gemfile). Set CRONITOR_API_KEY to an SDK Integration key from Settings → API before the process starts. The gem reads that variable when you require 'cronitor'. You can still assign Cronitor.api_key in the Sidekiq initializer. Do not commit the key.
Monitoring Ruby cron jobs
require 'cronitor'
# everything within the block is monitored,
# and errors are reported automatically
Cronitor.job 'important-job' do
ImportantJob.new(Date.today).run()
end
Monitoring Sidekiq jobs
# config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Sidekiq::Cronitor::ServerMiddleware
end
end
# Sidekiq Enterprise periodic jobs:
Sidekiq::Cronitor::PeriodicJobs.sync_schedule!
# sidekiq-scheduler gem:
Sidekiq::Cronitor::SidekiqScheduler.sync_schedule!
The middleware reports every job as it runs and creates a monitor on the first event if one does not exist. The sync_schedule! calls copy periodic schedules. They are optional if you are not using Enterprise periodic jobs or sidekiq-scheduler. The in-app steps are in monitoring Sidekiq.