I recently made the move from Linode to Heroku for my personal sites. The last time I checked out Heroku was about 2 years ago, which I later dismissed because at the time it seemed like having your own VPS was the right thing to do.
Having your own VPS sounds good in theory, but in reality it’s not, for me at least. I liked being able to throw up random projects, host my friends projects, and tinker with new frameworks and languages. I maybe did that once or twice out of the 4 years I had my own VPS. In reality, I’m way to busy to do any of that. I started to get lazy on updates, and security patches. The traffic my personal sites receive are so minimal they don’t need a VPS.
After my week at the Aloha Ruby Conf, I noted to myself to check out Heroku once again, to see what has changed. To my surprise, A LOT has changed. There’s the new cedar stack, updated gem which is really awesome to use, procfile, etc, I could go on.
Anyway, I read through all their documents, which are amazingly good, and then signed up to try their free tier, 1 Dyno, which seems like a perfect size for my site.
The first thing I had to do was switch my applications database from MySQL to PostgreSQL because Heroku has native support for PostgreSQL. You can use MySQL through one of their add-ons if you wish.
# gem 'mysql2' gem 'pg'
Then I got my site up on Heroku by logging in and deploying.
$ heroku login $ heroku create $ git push heroku master $ heroku run rake:db:migrate
I really like Unicorn(yhbt.net/unicorn/) as my webserver and was excited to see that they fully support it, and all I needed to do was add one line to my Procfile. I also want to mention that even though 1 Dyno is supposed to only have 1 level of concurrency, with Unicorn I can have multiple concurrency.
Add Unicorn the Gemfile.
I added a Unicorn config file to my project by creating
config/unicorn.rb. In the file I can configure the number of workers and timeout.
worker_processes 5 timeout 30 preload_app true before_fork do |server, worker| # Replace with MongoDB or whatever if defined?(ActiveRecord::Base) ActiveRecord::Base.connection.disconnect! Rails.logger.info('Disconnected from ActiveRecord') end # If you are using Redis but not Resque, change this if defined?(Resque) Resque.redis.quit Rails.logger.info('Disconnected from Redis') end sleep 1 end after_fork do |server, worker| # Replace with MongoDB or whatever if defined?(ActiveRecord::Base) ActiveRecord::Base.establish_connection Rails.logger.info('Connected to ActiveRecord') end # If you are using Redis but not Resque, change this if defined?(Resque) Resque.redis = ENV['REDIS_URI'] Rails.logger.info('Connected to Redis') end end
Now that Unicorn is set up, Heroku needs to use Unicorn instead of its default web server. To tell Heroku to use Unicorn, they’ve provided the Procfile. I created a file named
Procfile in the project root directory. Inside the web server can be configured.
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
After that’s pushed to Heroku, I can check to make sure Unicorn is running by using
$ heroku ps.
$ heroku ps > === web: `bundle exec unicorn -p $PORT -c ./config/unicorn.rb` web.1: up 2013/01/16 14:21:20 (~ 1h ago)
I also needed access to cron or something similar to schedule my nightly Rake that updates the photos on the front page to my latest Flickr photos. Once again that took me a few minutes to set up through their web interface.
First I created my rake task in
lib/tasks/flickr_update.rake. It’s a simple script that updates a table with the last 3 photos I posted to flickr. I’m using the gem ‘fleakr’ to do this. I have a RecentPhoto model that has the attributes: title, url(url to the photos flickr page), image_url(url to the exact location of the image file).
namespace :flickr do desc "update flickr photos on index page" task :update => :environment do Fleakr.api_key = 'xxxxxx' # replace with your Flickr API key user = Fleakr.user('xxxx') # replace with your Flickr Username RecentPhoto.destroy_all # remove all photos from table 3.times do |num| photo = user.photos[num] image_url = photo.medium.url image_url = image_url[0..-5] << "_q.jpg" # using the q version of this photo which is a square thumbnail. RecentPhoto.create(title: photo.title, url: photo.url, image_url: image_url) end end end
In the Heroku web application settings add the Scheduler add-on. Then add the heroku run command for that rake task.
That pretty much covers my move from Linode to Heroku. I highly suggest reading through the Heroku documents.