Most web apps have different highs and lows of traffic during the day. Maybe you get most of your traffic during daytime and not so much during the night and early morning. If you have this kind of situation it can be a good idea to adjust your number of dynos according to the changes in traffic in order to save a few bucks. When this is done automatic it is sometimes called auto-scaling or process scheduling.

There are a few services that can do this for you. However it is pretty easy to setup by your self, just using Heroku and some ruby code. And it can be for free.

Background

Putting it together

The scaling utility app

Create a new ruby project with a Gemfile:

source "https://rubygems.org"

ruby "2.0.0"

gem "heroku-api"

Create a ruby file “scaler.rb” that handles the API interactions:

#!/usr/bin/env ruby

require "bundler"
require "optparse"

Bundler.require(:default)

heroku_api_key = ENV["HEROKU_API_KEY"] || raise("No Heroku API key env variable set")

options = {}
OptionParser.new do |opts|
  opts.banner = "Usage: #{ARGV[0]} [options]"
  opts.on("-a", "--app NAME") { |v| options[:app] = v }
  opts.on("-t", "--type DYNO_TYPE") { |v| options[:type] = v }
  opts.on("-d", "--dynos NO_DYNOS") { |v| options[:count] = v }
end.parse!

raise OptionParser::MissingArgument, "--app" if options[:app].nil?
raise OptionParser::MissingArgument, "--type" if options[:type].nil?
raise OptionParser::MissingArgument, "--dynos" if options[:count].nil?

heroku = Heroku::API.new(api_key: heroku_api_key)
heroku.post_ps_scale options[:app], options[:type], options[:count]

Deploying

Create a new Heroku app and push the code:

$ heroku apps:create my-auto-scaler
$ git push heroku master

Setup your Heroku API key as environment variable, you find this on your account page:

$ heroku config:set HEROKU_API_KEY=my-api-key

Add the Heroku Scheduler add-on:

$ heroku addons:add scheduler:standard

Open up the schedulers setting page:

$ heroku addons:open scheduler:standard

Set up jobs for when your want to scale your app.

For example if you create a job with the task:

ruby scaler.rb -a my-app -t web -d 3

.. and set the next run to 5:00 UTC. The number of web dynos for the app “my-app” will be scaled to 3 at 5:00 in the morning UTC.

A little advice

If you are planning on doing this on a production app make sure you got some error tracking to get alerted if the scheduled tasks has failed to run. It is mentioned in the docs that “Scheduler is known to occasionally (but rarely) miss the execution of scheduled jobs”. For example I added e-mail alerts and logging to StatHat.