Heroku Scheduler

Last Updated: 19 March 2014

background cron

Table of Contents

Scheduler is an add-on for running jobs on your app at scheduled time intervals, much like cron in a traditional server environment.

Scheduler is a "best effort" service, meaning that execution is expected but not guaranteed. Scheduler is known to occasionally (but rarely) miss the execution of scheduled jobs. If scheduled jobs are a critical component of your application, it is recommended to run a custom clock process instead for more reliability, control, and visibility.

Scheduler add-on runs one-off dynos that will count toward your dyno-hours that you will be charged for each month.

A dashboard allows you to configure jobs to run every 10 minutes, every hour, or every day, at a specified time. When invoked, these jobs will run as one-off dynos and show up in your logs as a dyno named like scheduler.X.

Scheduler Dashboard

Installing the add-on

To use the Heroku Scheduler, install the add-on:

$ heroku addons:add scheduler:standard

Dyno-hour costs

Scheduler runs one-off dynos that will count towards your dyno-hours for the month. Dyno-hours from Scheduler tasks are counted just like those from heroku run or from scaled dynos. They will appear with a “scheduler” dyno type in your Heroku invoice.

Defining tasks

Tasks are any command that can be run in your application.

For Rails, the convention is to set up rake tasks. To create your scheduled tasks in Rails, copy the code below into lib/tasks/scheduler.rake and customize it to fit your needs.

desc "This task is called by the Heroku scheduler add-on"
task :update_feed => :environment do
  puts "Updating feed..."
  NewsFeed.update
  puts "done."
end

task :send_reminders => :environment do
  User.send_reminders
end

For apps built on other frameworks or languages, another convention is to add a script to bin/ that will perform the task. An example bin/clean-sessions script:

#!/usr/bin/env ruby
require "sequel"
DB = Sequel.connect ENV["DATABASE_URL"]
puts "Cleaning old sessions..."
DB["DELETE FROM sessions WHERE last_seen_at < ?", Time.now - 24*60*60]
puts "done."

Testing tasks

Once you’ve written your task and see that is functioning locally, the next step is to deploy your application and test your task on Heroku. To do so, use heroku run to run your task on Heroku:

$ heroku run rake update_feed

The scheduler uses the same one-off dynos that heroku run uses to execute your jobs, so you can be assured that if it works with heroku run, it will work from the scheduler.

Scheduling jobs

To schedule a frequency and time for a job, open the Scheduler dashboard by finding the app in My Apps, clicking “General Info”, then selecting “Scheduler” from the Add-ons drop down. The dashboard can also be opened from the command:

$ heroku addons:open scheduler

On the Scheduler Dashboard, click “Add Job…”, enter a task, select a frequency, dyno size (1X or 2X), and next run time.

Note that the next run time for daily jobs is in UTC. If you want to schedule the job at a certain local time, add the proper UTC offset.

For example, add rake update_feed, select “Hourly” and “:30” to update feeds every hour on the half-hour. Then add rake send_reminders, select “Daily” and “00:00” to send reminders every day at midnight.

Instead of specifying the command, you can specify a process type. The command associated with the process type will then be executed, together with any parameters you supply. See the syntax for one-off dynos to learn more.

Inspecting output

Logs for scheduled jobs go into your logs as process scheduler.X:

$ heroku logs --ps scheduler.1
2011-02-04T14:10:16-08:00 heroku[scheduler.1]: State changed from created to starting
2011-02-04T14:10:16-08:00 app[scheduler.1]: Starting process with command `bin/clean_sessions`
2011-02-04T14:10:19-08:00 app[scheduler.1]: Deleting stale sessions...
2011-02-04T14:10:27-08:00 app[scheduler.1]: done.
2011-02-04T14:10:28-08:00 heroku[scheduler.1]: State changed from up to complete

The scheduled dyno is also visible with the heroku ps command:

$ heroku ps
=== scheduler: `bin/clean_sessions`
scheduler.1: complete for 5m

=== web: `bundle exec thin start -p $PORT -e production`
web.1: idle for 3h

Long-running jobs

Scheduled jobs are meant to execute short running tasks or enqueue longer running tasks into a background job queue. Anything that takes longer than a couple of minutes to complete should use a worker dyno to run.

In order to prevent run-away jobs, jobs that run longer than their frequency will be terminated. For example, a job that runs every 10 minutes will be terminated after running for 10 minutes.

Known issues and alternatives

Scheduler is a best-effort service. There is no guarantee that jobs will execute at their scheduled time, or at all. Scheduler has a known issue whereby scheduled processes are occasionally skipped.

An alternative to Heroku Scheduler is to run your own custom clock process. This provides greater control and visibility into process schedule, and is production deployments in which scheduled jobs are a critical component. Please see this article for more information.