Cron Jobs on Heroku

Heroku is an App Engine for Ruby on Rails … and other Languages and Frameworks. Heroku is running your application and you don’t have to care about hardware or IT-Infrastructure. It is one abstraction layer above the Amazon Cloud EC2.

First of all, there are no cron jobs on Heroku. Because it is an App Engine you don’t have access to the linux os and the native cron daemon or crontab. Forget it! You have to use heroku worker dynos. Keep reading.

Usually you push your rails app via git to Heroku like this:

git push heroku master

and then it will be deployed on x web dynos. There are different kind of dynos. The default is “web”. If you want to do some background jobs you need a “worker” dyno. You can add worker dynos with this command:

heroku ps:scale worker=1

Now you have to create a Procfile in the root of your application. That can look like this:

web: bundle exec rails server -p $PORT
worker: bundle exec rake do_work

The Procfile is defining the different types of dynos which are available for your application. With “heroku ps:scale” you can scale the dyno types. If you want to have 7 web dynos and 1 worker just execute this:

heroku ps:scale web=7 worker=1

And Heroku will immediately deploy your web application on 7 web dynos and your background job on 1 worker dyno. That is fucking awesome!

All right we added one worker dyno and we defined in the Procfile that “bundle exec rake do_work” should be executed on the worker dyno. Now we just have to define the task “do_work” in the Rakefile.

There are different GEMs for scheduling jobs. For example clockwork, rufus and Qu. I prefer a more simple/native way. With a couple lines of code you can write your own scheduler. This rake task here is executing jobs at a given time. In this example every day at 07:00 AM.


task :do_work => :environment do
  puts "START"

  start_hour = 7
  start_minute = 0

  until 2 < 1 do
    now = Time.now
    hour = now.hour
    minute = now.min
    if hour == start_hour && minute == start_minute

      puts " do work #{Time.now}"

      # execute code here !!!

      if Time.now.hour == start_hour && Time.now.min == start_minute
        sleep(60)
      end
    end
  end
end

Just replace the comment with your code. And push it to heroku

heroku maintenance:on
git push heroku master
heroku maintenance:off

That’s it.

Published by Robert Reiz

CEO @ VersionEye. Passionated software developer since 1998.

Leave a comment