Heroku Scheduler
Last updated September 09, 2024
Table of Contents
Scheduler is a free add-on for running jobs on your app at scheduled time intervals, much like cron
in a traditional server environment.
While Scheduler is a free add-on, it executes scheduled jobs via one-off dynos that will count towards your monthly usage.
Scheduler job 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.
Once you access its interface, a dashboard will allow 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
.
Installing the add-on
Using the CLI
$ heroku addons:create scheduler:standard
Using the Dashboard
Navigate to the “Resources” tab of the app’s Dashboard. Search for “Heroku Scheduler” in the Add-ons search box. Follow the prompts to provision the Add-on.
Dyno hour costs
Scheduler runs one-off dynos that will count towards your usage 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.
In the Common Runtime, we expand the values of config vars before logging the executed command to the app’s log stream for audit purposes. Avoid using direct references to sensitive environment variables when defining your tasks.
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, 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.
A dyno started by scheduler will not run longer than its scheduling interval. For example, for a job that runs every 10 minutes, dynos will be terminated after running for approximately 10 minutes.
Note that there is some jitter in the dyno termination scheduling. This means that two dynos running the same job may overlap for a brief time when a new one is started.
Known issues and alternatives
Scheduler is a free add-on
Scheduler is a free add-on with no guarantee that jobs will execute at their scheduled time, or at all:
- In very rare instances, a job may be skipped.
- In very rare instances, a job may run twice.
Heroku Scheduler and Container Registry
If you are using Heroku Scheduler and Container Registry as your deployment method, your task must be accessible from the web
image. There is no way to specify a non-web image for task execution.
Alternatives to Heroku Scheduler
An alternative to Heroku Scheduler is to run your own custom clock process. This provides greater control and visibility into process scheduling, and is recommended in production deployments in which scheduled jobs are a critical component. Please see this article for more information.
The Dynos category In the Elements Marketplace has several add-on solutions for scheduling tasks and running processes on your behalf.