Believe it or not, but my mom has quite a lot of websites. I must have built 8-10 websites for my dear mother during the past 10 years, and currently she has three active ones. Most of them has been built in static HTML, a more recent one is a Rails application and one is no more than a Word document exported as a webpage. Now, a mother loves every excuse to get in touch with her son, but I have grown a bit weary of making minor text changes to one of her websites every few weeks.
Which is why I decided make life easier for myself and for my mom. I have never been fond of content management systems, especially not complex ones, but during the past year I have developed quite a lot of websites using Ruby on Rails based Radiant CMS at work, and I find it very simple to use and easy to extend.
Rails applications has never been easy to deploy, especially not on a shared host, so I have been following the development of Phusion Passenger, a.k.a. mod_rails, for the past months with a growing interest. I was quite happy when Dreamhost recently announced that they now offered to run Rails applications using Passenger, and I was eager to try it out - which is what I did today.
Now, I didn’t expect to be hard to get Radiant up and running on Dreamhost with Passenger for a seasoned Rails developer and Dreamhost user such as myself, but I found it in fact to be ridiculously easy - which is why I had to write this long introduction about my mom, as there is really not much to say. Anyhow I want to take the opportunity to explain exactly the configuration I selected and how I got it running.
I am running Radiant version 0.6.7 (the newest as I am writing this, always go for the newest) with two vital extensions: Gallery, for facilitating easy upload and display of images, and MultiSite, for being able to host all three of my mother’s websites within a single Radiant instance. Also, I of course wanted to deploy from a Subversion repository using Capistrono, as nothing is sweeter than writing cap deploy and get all the heavy deployment lifting done automatically.
The basics of running Passenger on Dreamhost is explained in the Dreamhost wiki: You create or edit a domain and check the Ruby on Rails Passenger option. You must also set the web directory to be path/to/rails/application/current/public, as Capistrano deploys to the current directory, and Passenger must be pointed to the public directory of the current version.
Let’s create a new Radiant CMS Rails application and add our extensions. I don’t use the Radiant gem, as gems can be troublesome to manage on Dreamhost, I simply use a fresh checkout of the version I want:
svn export http://svn.radiantcms.org/radiant/tags/rel_0-6-7/radiant/ radiant cd radiant git clone git://github.com/pilu/radiant-gallery.git vendor/extensions/gallery svn export http://svn.radiantcms.org/radiant/tags/rel_0-6-7/extensions/multi_site/ vendor/extensions/multi_site
There is one minor snag which will undoubtedly be fixed in the near future. The MultiSite extension is not compatible with Radiant 0.6.7, and two things need to be done to get it up and running: First, the HAML plugin bundled with the extension must be deleted, as it conflicts with the one now bundled with Radiant:
rm -rf vendor/extensions/multi_site/vendor/plugins/haml/
Second, three lines must be outcommented in vendor/extensions/multi_site/lib/multi_site/page_controller_extensions.rb so that the first few lines looks like this:
module MultiSite::PageControllerExtensions
def self.included(base)
base.class_eval {
alias_method_chain :index, :root
alias_method_chain :clear_model_cache, :site
alias_method_chain :continue_url, :site
# %w{remove clear_cache}.each do |m|
# alias_method_chain m.to_sym, :back
# end
}
end
...
end
Both extensions need to copy some files to the public directory of the application before we continue:
rake radiant:extensions:update_all
Now, let’s check our Radiant application into a Subversion repository and deploy it Capistrano. I cooked up a Capistrano recipe based on a mix of older recipes. Run capify . and copy and modify this code into config/deploy.rb:
set :user, 'my_dreamhost_user'
set :application, "my-passenger-domain.com"
set :repository, "my_svn_repository_url"
set :svn_username, "my_svn_username"
# =============================================================================
# You shouldn't have to modify the rest of these
# =============================================================================
role :web, application
role :app, application
role :db, application, :primary => true
set :deploy_to, "/home/#{user}/#{application}"
# set :svn, "/path/to/svn" # defaults to searching the PATH
set :use_sudo, false
# set :restart_via, :run
set(:svn_password) { Capistrano::CLI.password_prompt }
# saves space by only keeping last 3 when running cleanup
set :keep_releases, 3
# issues svn export instead of checkout
set :checkout, "export"
# keeps a local checkout of the repository on the server to get faster deployments
set :deploy_via, :remote_cache
ssh_options[:paranoid] = false
# Shared dirs between releases
set :shared_dirs, ['galleries']
# =============================================================================
# OVERRIDE TASKS
# =============================================================================
namespace :deploy do
desc "Restart Passenger"
task :restart, :roles => :app do
run "touch #{current_path}/tmp/restart.txt"
end
desc <<-DESC
Deploy and run pending migrations. This will work similarly to the \
`deploy' task, but will also run any pending migrations (via the \
`deploy:migrate' task) prior to updating the symlink. Note that the \
update in this case it is not atomic, and transactions are not used, \
because migrations are not guaranteed to be reversible.
DESC
task :migrations do
set :migrate_target, :latest
update_code
migrate
migrate_extensions # Added to support Radiant Extensions
symlink
restart
end
desc "Migrates Radiant Extensions"
task :migrate_extensions do
rake = fetch(:rake, "rake")
rails_env = fetch(:rails_env, "production")
migrate_env = fetch(:migrate_env, "")
migrate_target = fetch(:migrate_target, :latest)
directory = case migrate_target.to_sym
when :current then current_path
when :latest then current_release
else raise ArgumentError, "unknown migration target #{migrate_target.inspect}"
end
run "cd #{directory}; #{rake} RAILS_ENV=#{rails_env} #{migrate_env} db:migrate:extensions"
end
desc "Tasks to execute after initial setup"
task :after_setup do
# create shared dirs
shared_dirs.each do |dir|
run "mkdir -p #{shared_path}/public/#{dir}"
end
end
desc "Tasks to execute after code update"
task :after_update_code do
# relink shared dirs
shared_dirs.each do |dir|
run "rm -rf #{release_path}/public/#{dir}"
run "ln -nfs #{shared_path}/public/#{dir} #{release_path}/public/#{dir}"
end
end
end
This deployment script does a few clever things. All of it is borrowed from various places, but I simply don’t remember who to give credit for the different bits anymore, so - just don’t give me any other credit than explaining it. It overrides the migrations task to also invoke the new migrate_extensions task, making sure that all extensions also have their migrations run. It ensures that any uploaded images into public/galleries is shared between releases using a symlink, as uploaded images would otherwise disappear between releases. Finally, it restarts the Phusion Passenger instance by creating or changing a file called tmp/restart.txt, which is Passenger’s solution for allowing easy restart on shared hosts with limited access rights. Clever.
Also, don’t forgot to delete unwanted database.yml files in the config directory. Deploying on Dreamhost, you want to rename database.mysql.yml to database.yml and delete the others. Remember to create a MySQL database with an appropriate user using the Dreamhost Panel, and to write host: mysql.mydomain.com instead of socket: /tmp/mysql.sock for the production part of the file.
Okay, we are ready to deploy to deploy our application:
cap deploy:setup cap deploy
Now, since both Radiant itself and the Gallery has interactive installation scripts, we need to ssh into the Dreamhost server and run these scripts manually:
ssh my_dreamhost_user@my-passenger-domain.com cd my-passenger-domain.com/current rake production db:bootstrap rake production radiant:extensions:gallery:install
Log out of the server and do another Capistrano deployment, this time also running migrations:
cap deploy:migrations
And that’s it. You should now have your very own Radiant application running on Dreamhost, and with the MultiSite extension you can even manage multiple sites from the same physical Rails instance. All you need to do is mirror domains to the application and create new sites within the administration panel.
If you have any problems following these steps, please let me know. I might be able to help you out.
UPDATE: You don’t have to use Subversion and Capistrano to deploy Radiant on DH, especially not if you aren’t going to use any extensions and will only deploy once. Take a look at the Radiant wiki page on deploying to Dreamhost to see which steps you can skip to just upload once using SFTP.
Hello, I'm Casper Fabricius. I have developed for the web for 8 years, and have been enjoying Ruby on Rails for the past 3.
My experience covers communities, shopping solutions, multi-language sites, heavy back-end lifting and a wide selection of more traditional websites. I currently favor Radiant CMS as a platform, and I am an expert Radiant extension developer.
I am based in Copenhagen, Denmark, but I take assignments from across the globe. Feel free to study my resumé, featured projects and - of course - to hire me.