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.
gem 'unicorn'
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.
Side note: The only downside to Heroku that I encountered was not being able to write to disk. Since my site is entirely static, the comments are javascript, I relied on page caching to keep the load time fast. When I switched to Heroku I had to disable page caching because I’m not able to write to the cached html pages to disk.