Rails and Capistrano on Site5

So I just moved my hosting from Dreamhost to Site5. Thus far everything has been stellar. However, when I was looking over the forums to figure out how Capistrano fits at Site5, I noticed people doing things are slightly weird way (weird for the Rails world at least). It is probably due to this tutorial on how to get Rails apps working on Site5.

I can also say that I have the turbo charged plan, which has a different methodology for multi-site hosting and management. From the dates on the forums, the tutorial and most posts I saw could be doing things the “weird” way because they are on the regular hosting plan, etc.

So, on the turbo charged plan, here is a way you can set up your rails app so that Capistrano will work mostly with its defaults (without overriding a lot of variable values).

  1. Create a subdomain, for example app.jason.com
  2. In the public_html/app/ folder create a current directory.
  3. Edit the subdomain’s document root to include the new current directory.
  4. Now delete the current directory.
  5. Use Capistrano to deploy your app. It will create a current symlink where there used to be a current directory.

Now everything will just work. That is of course if you configured Capistrano correctly. But you can find plenty of other articles on how to do that.

Using Capistrano to Overcome Deployment Hurdles on Dreamhost

While I have been happy with Dreamhost's services, let's face it, deploying a Rails app to a shared Dreamhost server can be a rather daunting task. Even after I succeeded for the first time, I could not duplicate that success on a dependable basis. Then I found Capistrano, many thanks to Jamis Buck.

"Capistrano is a utility that can execute commands in parallel on multiple servers." It's original purpose was deploying web apps, but, it can do much more. However, within the scope of this article, we will explore its uses for deploying web apps to Dreamhost. Many of the techniques used in the article are just my preference for using Capistrano and can be set up differently for your own use.

For this example we will assume an application name of 'blog' and a domain name of 'example.com'. We will set up the app as a sub-domain of example.com.

The first order of business is to set up Capistrano on your machine. Look here for instructions to do that.

Next we must 'Capistranize' your project on your development (local) machine. This step will add a deploy.rb (the deployment recipe) file to your app's config/ directory. It also adds capistrano.rake to your lib/tasks/ directory. Simply navigate to your project's root at a command line a type the following command. Don't overlook the period in the command, it is referring to the path to your app.


cap --apply-to . blog

Now we will set up the new domain on Dreamhost. Go to the manage domains section of the Dreamhost panel and make your settings match this. The key thing that we have done here that we would not do for a non-Capistrano deployment is point the web app to the blog.example.com/current/public/ directory. This current/ directory is actually a symlink that points to a real directory whose name is derived from the time the app was deployed. This real directory will be under the release/ directory, which is at the same level as the current symlink in the directory structure. This will allow us to keep several deployments in the directory and point to the current deployment using the current symlink.

Once you hae saved the new domain at the Dreamhost control panel, you will need to ssh into your Dreamhost account and delete the real current directory that was created for you by the panel. If you do not do this, Capistrano will not be able to create a symlink due to a naming conflict. To delete the real directory, navigate to your apps root directory and issue a recursive remove command, as there is also a public directory underneath the current directory.


rm -r current

Now we are going to set up our deployment recipe. Open the deploy.rb file. You will notice that the syntax is just pure Ruby syntax. The first set of variables we will configure is required for every deployment recipe.

1
2
3
4
5
set :user, "bloguser"
set :application, "blog.example.com"
set :repository, "http://svn.example.org/mephisto/trunk"
set :svn_username, "bloguser"
set(:svn_password)

We will set the user to 'bloguser' because we specified this user when we set up the apps domain in the Dreamhost panel. You can use whatever user you want to as long as it has access to read, write and delete in and below the blog apps root directory. We also configured the name of the app, the subversion repository URL and user name, and told Capistrano to prompt for the subversion password. It is important to note that Capistrano is going to deploy from the repository, not our development machine, so you must have all code you want deployed committed prior to actually trying to deploy.

The next section of variables we will configure is the roles. You can set up any number of roles that your configuration will require. According to the Capistrano manual, "You can define whatever roles you want, but the default Capistrano tasks look for those three: :app, :web, and :db. The :app role describes which servers are acting as the application servers (the servers running the FastCGI instances). The :web role describes the servers running Apache, and the :db role describes the servers running your database(s)."

1
2
3
role :web, "#{application}"
role :app, "#{application}"
role :db,  "#{application}", :primary => true

On Dremhost shared hosting, all of our roles are on the same machine and that machine has the same name as the app, so I reuse the application variable.

The next section of variables are optional, as each of them has a default value if not specified here.

1
2
3
4
5
set :user_root, "/home/#{user}"
set :deploy_to, "#{user_root}/#{application}"
set :use_sudo, false
set :checkout, "export"
set :restart_via, :run

The user root variable is the path to where the workspace for your applications user is. For us, it will be 'home/bloguser/'. So we reuse the user variable to keep our configuration DRY. The deploy to variable is the directory where we want to deploy our application. due to our choices in creating the domain at Dreamhost, the folder name the app will live in is that same as the application variable, so we will reuse it. The user sudo variable let's Capistano know if it should use the sudo utility to perform admin functions, which we cannot do at Dreamhost. the Checkout variable lets Capistrano know whether to check out or export from the repository. Of course, an export will save disk space and bandwidth.

Next comes the user variables section where you can define variable specific for tasks that you add to the deployment.

1
2
3
4
5
6
set :db_adapter, "mysql"
set :db_host, "mysql.#{application}"
set :db_name, "blog"
set :db_dev_postfix, ""
set :db_test_postfix, "_test"
set :db_prod_postfix, ""

We are going to set up variables that will aid us in dynamically creating a database.yml file at deployment time, an idea I got from Jeremy Voorhis. Using a standard naming convention for your web apps and resources at Dreamhost will help make your life easier. I always name the MySQL host mysql.[application name]. This is reflected in the db host variable. The db name variable is the root name of your database, while the postfix variables are the postfix for each environment's database. Because my production and development database are always on separate machines, I just name them without a post fix. Only my test database has a postfix.

Next we define custom operations using the after_update_code task that Capistrano calls by default.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
desc "Tasks to execute after code update"
task :after_update_code, :roles => [:app, :db, :web] do 
        
 # update the...
 run "cp #{user_root}/_resources/.htaccess #{release_path}/public/.htaccess"
 run "cp #{user_root}/_resources/dispatch.fcgi #{release_path}/public/dispatch.fcgi"
 
 #put the...
 run "cp -r #{user_root}/_resources/tmp #{release_path}/tmp"
 
 # delete the..
 #run "rm #{release_path}/.project"
 
 # fix permissions...
 run "chmod +x #{release_path}/script/process/reaper"
 run "chmod +x #{release_path}/script/process/spawner"
 run "chmod 755 #{release_path}/public/dispatch.*"
 
 database_yml_change
 
end

Here, I am copying the .htaccess and dispatch.fcgi files that I have stored in the _resources/ directory that is located in my user root at Dreamhost. I have to do this because I develop on a windows machine. We also copy the tmp/ directory from this same location. I do not like to source control it so that I do not have to keep ignoring files, etc. I also delete the .project file that Eclipse IDE creates. I like to source control this so that I back up how I have my project set up. You can do this same thing with any IDE files you may have or just comment it out. Next we fix the permissions on several files. Last we call the database_yml_change task, which is a custom task we created and will review later in this article. Next we touch the dispatch.fcgi files after a deploy and after a rollback. This helps the app to restart on Dreamhost. I have sometimes run into trouble where it takes an app a little while to restart and this seems to help minimize this. I have yet to figure out how to get the reaper or spawner scripts to work correctly, so this is the next best thing.

1
2
3
4
5
6
7
8
9
desc "Restarting after deployment"
task :after_deploy, :roles => [:app, :db, :web] do
  run "touch /home/#{user}/#{application}/current/public/dispatch.fcgi"
end

desc "Restarting after rollback"
task :after_rollback, :roles => [:app, :db, :web] do
  run "touch /home/#{user}/#{application}/current/public/dispatch.fcgi"
end

In the last part of the deployment recipe we create the database.yml file dynamically from out settings and put it into the shared/config/ directory, as all versions of the app that we roll out can share this file. We then create a database.yml symlink in the current/config/ folder that points to the shared/config/database.yml file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
desc "Create database.yml in shared/config" 
task :database_yml_change do

  # remove  the dev version of database.yml
        run "rm #{release_path}/config/database.yml"

  database_configuration = render :template => <<-EOF
login: &login
  adapter: <%= db_adapter %>
  host: <%= db_host %>
  username: <%= user %>
  password: <%= password %>

development:
  database: <%= "#{db_name}#{db_dev_postfix}" %>
  <<: *login

test:
  database: <%= "#{db_name}#{db_test_postfix}" %>
  <<: *login

production:
  database: <%= "#{db_name}#{db_prod_postfix}" %>
  <<: *login
EOF

  run "mkdir -p #{deploy_to}/#{shared_dir}/config" 
  put database_configuration, "#{deploy_to}/#{shared_dir}/config/database.yml" 
  
  # link to the database.yml
  run "ln -nfs #{deploy_to}/#{shared_dir}/config/database.yml #{release_path}/config/database.yml" 
end

The next thig we need do is set up the directory strucure on the Dreamhost side. Luckily, Capistrano provides a rake task to do this that will now work as we have configured everything it needs. Simply navigate to the root of your app on your development machine an enter this command.

1
2
rake remote:exec ACTION=setup #deprecated
cap setup #this is the newer way

This will create the releases/ and shared/ directories under the blog.example.com/ directory for our example.

The last thing you must do to start your first deployment is type this command on your development machine (don't forget to commit everything you expect to deploy).


cap deploy

If everything was set up correctly, your app will be deployed to Dreamhost and you should be able to browse to it. The app may delay due to starting up. In addition, you have created a repeatable process that is dependable to roll out all of your changes and even roll back if you create an issue with a deployment.

Here is the full deploy.rb file from this article.