Managing Your Production Crontab with Whenever and Capistrano

Managing Your Production Crontab with Whenever and Capistrano

Though Cron has been around since the 1970s, it is often still a perfectly viable, incredibly robust way to run scheduled jobs for your web application – in particular if you deploy your app to a single dedicated server or VPS.

Whenever is a command line utility, written in Ruby, that lets you declare your Crontab entries using the very human-readable Ice Cube DSL, and that includes related utilities to actually write out to your Crontab based upon these human-friendly source files. The benefits for your project are twofold. First, you get to define your Crontab entries in a language that is natural to read rather than in the famously obscure and forgettable native syntax. Second, you get to keep your schedule definition within your project tree so that it becomes, in effect, integrated with your application rather than something managed separately with your infrastructure.

In this post we will walk through Whenever configuration, an example schedule file, and check out a complete production deployment solution using Whenever’s Capistrano integration. Though you can use the Whenever gem independent of a Ruby on Rails app, this demo will assume we are working with a Rails app so, to start, we will install Whenever via our Gemfile with

# in Gemfile...

gem 'whenever', require: false

…then run bundle install. We’ll then run the “wheneverize” command from our project root:

# from the shell your project root...

$ wheneverize .

This will drop a new file in your project tree at config/schedule.rb to which we can add our crontab rules. Let’s say our app sends appointment reminders on the hour to everybody who has an appointment between 1 and 2 hours in the future. We might implement such a task with rake:

# in lib/tasks/appointments.rake...

namespace :appointments do 
  task send_reminders: :environment do
    Appointment.where(start_time: (1.hour.from_now..2.hours.from_now)).each do |appointment|
      appointment.send_reminder!
    end
  end
end

Now we can go to our config/schedule.rb file and add a pseudo Crontab entry to indicate we want cron to run this task hourly:

# in config/schedule.rb...

every 1.hour do
  rake "appointments:send_reminders"
end

In addition to its rake macro, Whenever ships with command and runner macros, where command executes a shell command and runner executes whatever plain Ruby string you pass it within the context of your application environment.

To write a Crontab entry locally based upon your schedule.rb file, run:

$ whenever --update-crontab

 

For Capistrano Deployments

Whenever ships with a seamless Capistrano integration so that you can have the crontab on your production or staging servers updated automatically whenever you push a deploy. To leverage this feature, require ‘whenever/capistrano’ in your Capfile:

# in Capfile

require "whenever/capistrano"

You should be sure to set a whenever_identifier in your schedule.rb file:

# in config/schedule.rb...

set :whenever_identifier, ->{ "#{fetch(:application)}_#{fetch(:stage)}" }

# ...

This value is used to delimit the generated Crontab entries within the Crontab file.

Whenever’s Capistrano integration gives you a whenever:update_crontab Capistrano task that will generate and write your Crontab entries on the remote host. Though you can execute this task manually, it also will automatically execute on any deploy or rollback.

Though this was a high-level and brief overview of Whenever and its Capistrano integration, remarkably this covers a relatively comprehensive and real use case. More in-depth documentation around features not explored here can be found on the project’s Github page.

Nicholas

Hi! I'm Nicholas and I like building stuff. I spent a decade working with startups in NYC as a developer before turning my attention to seed-stage investing beginning in 2018. I write here on topics including startups, investing, travel, software development, and just about any other matter of personal relevance. I can be reached by email at [email protected].

No Comments

Comments Closed