Deploying CodeIgniter Projects to Dreamhost Using Capistrano
Originally posted on 2008-03-27 at masonbrowne.info
As promised, this article will attempt to explain how to deploy PHP projects using Capistrano. Specifically, I'll be going through the steps to deploy a CodeIgniter project to DreamHost using Capistrano, because that is the environment I have most readily available and it can be modified to suit almost any code-base or hosting environment.
Why Deploy PHP Projects Using Capistrano?
Convenience, robustness, speed, 1337-factor. One of the main fringe-benefits of using Capistrano for deployment is that it forces you to keep your code in source-control (Subversion, in this case), and that's hardly ever a bad thing. Additionally:
- You can deploy changes with a single command.
- You can easily specify multiple stages of deployment (e.g., dev, staging, live) using the capistrano-ext gem. (See: Jamis' post.)
- You can easily roll-back deployments.
- It's super kick-ass when you have multiple application servers that you need to keep in sync.
- You can automate other tasks besides deployment
It's just... one of those tools which, once added to your arsenal, is almost painful to live without.
Things You'll Need
If you're following along from home, you'll need the following:
- A Computer
- A terminal (I use iTerm)
- A text editor (I use TextMate)
- CodeIgniter (I'm using version 1.6.1, only because it's the latest-and-greatest)
- Subversion client
- Subversion repository (I'm using a DreamHost repository)
- A DreamHost account (or other hosting account with SSH and PHP installed)
- Ruby (1.8.5 or later)
- RubyGems (1.0 or later)
- Capistrano (2.0 or later)
This may seem like a lot, but if you're lucky, you'll already have most of these things. If you're unlucky, take solace in the fact that you'll only have to install them once.
A Computer
I included this in the list solely to say that I'm using Mac OS X as my development setup. Capistrano should work with any POSIX-compliant system with Ruby. To my knowledge, it also works with Windows, but I think you'll get the most pain-free experience using Linux or Mac OS X. The important part is that you're deploying to a POSIX-compliant system. Having the same locally is more for the convenience of dev-prod cohesion.
A Terminal
I'm going to go ahead and assume you're at least semi-savvy with the command-line. You should be able to comfortably maneuver around the CL, create and destroy directories, etc.
I use iTerm (on the Mac) because I'm a fan of tabbed terminal windows, and have been using it for a long time. Really, it's a matter of personal preference.
A Text Editor
Preferably something that lets you open folders of files as a project. I use TextMate. It was the first decent text editor I found after switching away from Eclipse, and it suits me nicely. I've heard BBEdit is also nice. When I'm on a Linux box, I usually end up using Kate.
CodeIgniter
As I've previously written:
I would highly recommend introducing a beginner to a low-weight framework, such as CodeIgniter. While it may seem confusing at first to throw someone into the land of config files, mod_rewrites, and design patterns, I believe the benefit outweighs the negative.
Main/First reason: Using a framework introduces structure. Getting used to structured code is essential as a beginner, lest you have to forget all you’ve learned to do something better later. The MVC pattern is pretty much the de facto one for web development these days, and an early start in this is nothing but good.
Second: A framework provides a directed base for the beginner. Instead of the huge-mongous PHP developer community, who could be working on anything from algorithms to database interfaces to language implementation, framework users have a community of other framework users who are presumably using it for the same purpose: to build a dynamic web site or web application. In the case of CodeIgniter, both the forums and the extensive documentation serve as excellent starting points for a befuddled developer, new and old.
Lastly: Developing in a framework is more fun. Okay, this one is largely personal, but I have the feeling it applies to many developers. Which is more fun? Developing an idea? Or developing something that will let you work on that idea? Would you rather build a boat now? Or build all the power tools, grow the wood, and harness the power beforehand? Using and becoming accustomed to a framework lets you get started on developing the idea, the application, right away, without having to worry about all the gritty details.
I prefer CodeIgniter over Symfony and CakePHP. While they all have their strengths, I believe CodeIgniter has the clearest, cleanest implementation with minimal magic or mixed metaphors. Beginners will appreciate its simple, understandable setup, and experts will love the clarity of its guts, as well as the ease with which they can extend it. That said, this tutorial should work with just about any centralized code-base you're working with, so if you're a Symfony/CakePHP fan, go for it. This tutorial will not deal with setting up Phing/Propel, so Symfony developers should look elsewhere for that information.
Subversion
The steps required to get Subversion on your machine vary from very easy to insanely complex. Most architectures will have a simple installer. Linux distributions with a package-managing utility should definitely find it in their distro-specific repositories.
I'm not going to explain how to install Subversion on your machine. Performing a Google search will give you just about all the information you need. If you can check out projects from a repository, you're set to go.
As For The Repository...
You're going to need a subversion repository that's accessible both your machine and your server. I'm just using the repository tools my DreamHost provides. (In fact, that's about all I use DreamHost for these days, along with this blog. The downtime lately has been a bit ridiculous.) Edit: This blog is no longer hosted on DreamHost. =)
A DreamHost Hosting Account
Again, I'm using DreamHost because it's the only remote-box I have that is running PHP. The rest are VPSs running bare nginx/Mongrel. This tutorial should work equally well if you're running on just about any server with SSH and PHP. You'll just have to substitute the steps for setting up your document root with whatever is appropriate for your setup.
Ruby
Capistrano is a Ruby application, so you'll need this on your development machine. If you're running Mac OS X or Linux, this is probably already installed.
RubyGems
RubyGems is the de facto method for distributing Ruby libraries. It's a very straight-forward process. Download the latest version, unzip it, and do a sudo ruby setup.rb.
Capistrano
Once you have RubyGems installed, getting Capistrano is a snap. Just open up a terminal window and run sudo gem install capistrano. It will fetch the latest version and install it.
The Process
We'll be doing the following, more or less in this order:
- Setting up CodeIgniter
- Setting up a domain for our project in our DreamHost account
- Creating a subversion repository for our CodeIgniter project
- Capifying our project
- Customizing our deployment recipe
- Deploying to the server
Setting Up CodeIgniter
Basically, this involves downloading the latest CodeIgniter release, unarchiving it, and putting it somewhere convenient.
Amandil:~/capcidemo sintaks$ ls
CodeIgniter_1.6.1.zip
Amandil:~/capcidemo sintaks$ unzip CodeIgniter_1.6.1.zip
Archive: CodeIgniter_1.6.1.zip
creating: CodeIgniter_1.6.1/
inflating: CodeIgniter_1.6.1/index.php
...
inflating: CodeIgniter_1.6.1/user_guide/userguide.css
Amandil:~/capcidemo sintaks$ mv CodeIgniter_1.6.1.zip my_project
Amandil:~/capcidemo sintaks$ cd my_project
Amandil:~/capcidemo sintaks$
I prefer to keep my application code outside my document root. It's just one of those "good practices" that everyone suggests for security purposes. Few people do this with PHP scripts, because typically they're running various PHP scripts directly, e.g., visiting http://example.com/blog.php or http://example.com/photos.php for a blog page and photo page, respectively.
While it is very possible to keep your code organized in this manner, CodeIgniter goes about things differently. Instead of executing code based on the PHP script you visit, it executes code based on cues given in the URL. By convention, it follows the pattern of http://example.com/[controller]/[action]/[id], where [controller] is a collection of actions themed around a common purpose, such as "blog". Actions for a blog might be index, view, edit, delete. By default, if you don't specify an action and id, it will default to index, so the index() function of your Blog object will be called. If this is slightly confusing, check out the CodeIgniter docs, or crack open an editor and take a look at system/application/controllers/welcome.php, which contains Welcome controller and index action. In order to view the output of this function, you would visit http://example.com/welcome or http://example.com/welcome/index.
But! I'm not here to explain how to use CodeIgniter, so let's move right along. Like I said, I like to keep my application code out of the document root, so let's go ahead and create an htdocs directory and move the index.php bootstrap file inside:
Amandil:~/capcidemo/my_project sintaks$ mkdir htdocs
Amandil:~/capcidemo/my_project sintaks$ mv index.php htdocs/
Amandil:~/capcidemo/my_project sintaks$
I'm also going to open the project directory in TextMate:
Amandil:~/capcidemo/my_project sintaks$ mate . &
[1] 5207
Amandil:~/capcidemo/my_project sintaks$
Since our bootstrap file moved, we have to tell it where to find the CodeIgniter system files. Open my_project/htdocs/index.php and change this:
/*
|---------------------------------------------------------------
| SYSTEM FOLDER NAME
|---------------------------------------------------------------
|
| This variable must contain the name of your "system" folder.
| Include the path if the folder is not in the same directory
| as this file.
|
| NO TRAILING SLASH!
|
*/
$system_folder = "system";
to this:
/*
|---------------------------------------------------------------
| SYSTEM FOLDER NAME
|---------------------------------------------------------------
|
| This variable must contain the name of your "system" folder.
| Include the path if the folder is not in the same directory
| as this file.
|
| NO TRAILING SLASH!
|
*/
$system_folder = "../system";
Booya. We're ballin' now. In order to make our URLs pretty, we'll add some mod_rewrite rules to forward all requests to our bootstrap file. Create my_project/htdocs/.htaccess with the following contents:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>
(Note: This was taken from the CodeIgniter wiki. For a bunch more tips, you can visit the page here.)
By default, CodeIgniter will generate URLs using a http://127.0.0.1/CodeIgniter/index.php/[controller]/[action]/[id] pattern.
We'll need to change the http://127.0.0.1/CodeIgniter/ portion to our URL, and get rid of the index.php portion, since we're using .htaccess.
Open my_project/system/application/config/config.php and change:
/*
|--------------------------------------------------------------------------
| Base Site URL
|--------------------------------------------------------------------------
|
| URL to your CodeIgniter root. Typically this will be your base URL,
| WITH a trailing slash:
|
| http://www.your-site.com/
|
*/
$config['base_url'] = "http://127.0.0.1/CodeIgniter/";
/*
|--------------------------------------------------------------------------
| Index File
|--------------------------------------------------------------------------
|
| Typically this will be your index.php file, unless you've renamed it to
| something else. If you are using mod_rewrite to remove the page set this
| variable so that it is blank.
|
*/
$config['index_page'] = "index.php";
to your URL and empty string for base_url and index_page, respectively:
/*
|--------------------------------------------------------------------------
| Base Site URL
|--------------------------------------------------------------------------
|
| URL to your CodeIgniter root. Typically this will be your base URL,
| WITH a trailing slash:
|
| http://www.your-site.com/
|
*/
// This is my domain for this tutorial. Swap it out for whatever you're using.
$config['base_url'] = "http://apps.vazav.com/";
/*
|--------------------------------------------------------------------------
| Index File
|--------------------------------------------------------------------------
|
| Typically this will be your index.php file, unless you've renamed it to
| something else. If you are using mod_rewrite to remove the page set this
| variable so that it is blank.
|
*/
$config['index_page'] = "";
That should be all you need to do with your CodeIgniter files. This step can (and should) be applied to any CodeIgniter project you do, not just those you're setting up in Capistrano.
Setting Up a Domain For Our Project in Our DreamHost Account
Crack open your browser, go to your DreamHost control panel, and navigate to Domains -> Manage Domains. I already have a subdomain set up, so I'm just going to tweak the settings. If you don't have a domain or subdomain ready, you can add one and mirror these settings, though it'll take some time for your DNS changes to propagate.
I'm using PHP 5.2.x, and have "Extra Web Security" and "FastCGI support" enabled (from a previous project). My SSH user is called celericp.

The important bit the part where it says "Specify your web directory". We'll be deploying to a very specific location, partially dictated by Capistrano.
When you deploy an application using Capistrano, it effectively deploys it to /user/specified/path/[application_name]/current. (I say "effectively" because it's actually putting the files in a directory named after the time at which you deploy, and creating a symlink called current to the latest version.) Since we want our document root to be htdocs, I set my web directory to /home/username/apps.vazav.com/capcidemo/current/htdocs:

As for www versus no-www, I recommend "Remove 'www'", but I have mine set to both for historical purposes. Save your settings. If you just added this domain, it'll take several hours before you can hit that URL. Since I already had the subdomain, my changes only take a few minutes.
One more bit of tricky business. When this project is deployed to my server, it's going to put the latest release at /home/celericp/apps.vazav.com/capcidemo/[timestamp], and create a symlink called current which points to it (resulting in the latest version being available at /home/celericp/apps.vazav.com/capcidemo/current). However, since we specified in the DreamHost panel that our document root is at /home/celericp/apps.vazav.com/capcidemo/current/htdocs, the DreamHost robots will automatically create the current/htdocs portion on the filesystem, which is not desired. Basically, as part of Capistrano's deployment process it'll try to delete current as though it's a symlink, and the system will complain that current is a directory, not a file. So we're going to SSH in and remove that current directory and everything under it.
(For the curious, this is an example of a task that could be preformed by Capistrano if you felt like writing the task for it. Since we're not there yet, I'll just do it manually.)
Amandil:~ sintaks$ ssh celericp@soto.dreamhost.com
Password:
[soto]$ cd apps.vazav.com/capcidemo/
[soto]$ ls
current
[soto]$ rm -rf current/
[soto]$
soto.dreamhost.com is the address I use to access my DreamHost domains. Your is likely different.
That's it for the server setup. When the time comes, we should be able to deploy, and then visit the latest deployment at http://apps.vazav.com
Creating a Subversion Repository For Our CodeIgniter Project
Again, as part of the deployment process, we'll be checking out our code from a subversion repository to our server, so we need to set up said repository, and get our project into source-control.
Navigate to Goodies -> Subversion and enter your desired settings. Since I keep all my pet projects under source control on http://labs.vazav.com, I'll be creating my repository there.

As always, be sure to pick a good password for your repository. If you're using ssh+svn instead of HTTP (which isn't an option on DreamHost), you can set up key-based authentication, which is spiffy, since you don't have to include your username and password in your Capistrano recipe file. For this tutorial, we'll just be living on the wild side.
Create your repository, and get some coffee while you wait for the DreamHost robots to make it. It shouldn't take more than ten minutes.
Now it's time for our initial check-in. I'm setting it up with the standard trunk, branches, tags setup, and removing the CodeIgniter archive that I had used earlier.
Amandil:~/capcidemo/my_project sintaks$ cd ..
Amandil:~/capcidemo sintaks$ rm CodeIgniter_1.6.1.zip
Amandil:~/capcidemo sintaks$ mv my_project trunk
Amandil:~/capcidemo sintaks$ mkdir tags
Amandil:~/capcidemo sintaks$ mkdir branches
Amandil:~/capcidemo sintaks$ svn import . http://labs.vazav.com/capcidemo --username deploy -m "initial checkin"
Authentication realm: <http://labs.vazav.com:80> CapCi Demo
Password for 'deploy':
Adding trunk
Adding trunk/license.txt
...
Adding branches
Adding tags
Committed revision 1.
Amandil:~/capcidemo sintaks$
With that, your project is now in subversion. If you're new to SCM, you're in for a treat. You can now track changes to your code-base over time, revert to previous versions, and share your project with teammates with minimal effort.
It's worth noting that Subversion isn't the only available option out there for Source Code Management. A rising star in the area is Git, which uses a decentralized model, unlike Subversion. I personally haven't tried it out, but it sounds good on paper. Linus Torvalds has a particularly entertaining talk he gave to Google, which you can view on YouTube.
Though we imported our work into subversion, the stuff on our filesystem is not under version control, so let's check out the code we just checked in.
Amandil:~/capcidemo sintaks$ rm -rf trunk/ tags/ branches/
Amandil:~/capcidemo sintaks$ ls
Amandil:~/capcidemo sintaks$ svn co http://labs.vazav.com/capcidemo/trunk
A trunk/license.txt
A trunk/htdocs
...
A trunk/user_guide/general/security.html
Checked out revision 1.
Amandil:~/capcidemo sintaks$
Capifying Our Project
This one is pretty simple. When you installed Capistrano via RubyGems, a couple executables were made available to you. One is cap, which is the command you use to run Capistrano tasks. The other is capify, which sets up a project for use with Capistrano. We'll be running the second.
Amandil:~/capcidemo sintaks$ cd trunk/
Amandil:~/capcidemo/trunk sintaks$ mkdir config
Amandil:~/capcidemo/trunk sintaks$ capify .
[add] writing `./Capfile'
[add] writing `./config/deploy.rb'
[done] capified!
Amandil:~/capcidemo/trunk sintaks$
You'll see I added a config directory to trunk. Capistrano requires it to generate the default deploy.rb recipe file.
We'll then add the newly generated files to subversion, just to be nice and friendly:
Amandil:~/capcidemo/trunk sintaks$ svn add Capfile config/
A Capfile
A config
A config/deploy.rb
Amandil:~/capcidemo/trunk sintaks$ svn ci -m "initial capistrano files" Capfile config/ config/deploy.rb
Adding Capfile
Adding config
Adding config/deploy.rb
Transmitting file data ..
Committed revision 2.
Amandil:~/capcidemo/trunk sintaks$
At this point your directory tree should look something like the following:

Customizing Our Deployment Recipe
Go ahead and open trunk/config/deploy.rb. The default recipe doesn't have much:

Change it to match the following:
set :application, "capcidemo"
set :repository, "http://labs.vazav.com/capcidemo/trunk"
set :user, "celericp"
set :domain, "apps.vazav.com"
set :scm_username, "deploy"
set(:scm_password){Capistrano::CLI.password_prompt("subversion password: ")}
set :deploy_to, "/home/#{user}/#{domain}/#{application}"
set :use_sudo, false
role :app, domain
Pretty self-explanatory so far. :application is the name of our application. :repository is the repository location. :user is the username Capistrano will use to SSH into our server. :domain is the domain we'll be SSHing into. :scm_username is the user Capistrano will try to check out the project as. When it hits the scm_password line, Capistrano will prompt you for your password. This is done so you don't have to store your svn password in a repository file. :deploy_to is set to the location we specified when setting up our DreamHost domain. By the time it gets to that line, :deploy_to will read as /home/celericp/apps.vazav.com/capcidemo.
If you're curious why the :scm_password line is different, here's the simple explanation: Instead of setting :scm_password to a concrete value, we want to prompt for it. But we don't want to prompt for it unless we actually use that value. By setting the variable in this fashion (using a code block), :scm_password will be lazily evaluated, and will prompt for the password only when it's needed.
At this point, you can run cap -T and see the cooked-in tasks that Capistrano comes with (don't run them!).
(If we hadn't set scm:password in the manner we did, it would have asked us for our subversion password, even though all we wanted was to see the available tasks.)
Amandil:~/capcidemo/trunk sintaks$ cap -T
cap deploy # Deploys your project.
cap deploy:check # Test deployment dependencies.
cap deploy:cleanup # Clean up old releases.
cap deploy:cold # Deploys and starts a `cold' application.
cap deploy:migrate # Run the migrate rake task.
cap deploy:migrations # Deploy and run pending migrations.
cap deploy:pending # Displays the commits since your last deploy.
cap deploy:pending:diff # Displays the `diff' since your last deploy.
cap deploy:restart # Restarts your application.
cap deploy:rollback # Rolls back to a previous version and restarts.
cap deploy:rollback_code # Rolls back to the previously deployed version.
cap deploy:setup # Prepares one or more servers for deployment.
cap deploy:start # Start the application servers.
cap deploy:stop # Stop the application servers.
cap deploy:symlink # Updates the symlink to the most recently deployed ...
cap deploy:update # Copies your project and updates the symlink.
cap deploy:update_code # Copies your project to the remote servers.
cap deploy:upload # Copy files to the currently deployed version.
cap deploy:web:disable # Present a maintenance page to visitors.
cap deploy:web:enable # Makes the application web-accessible again.
cap invoke # Invoke a single command on the remote servers.
cap shell # Begin an interactive Capistrano session.
Some tasks were not listed, either because they have no description,
or because they are only used internally by other tasks. To see all
tasks, type `cap -Tv'.
Extended help may be available for these tasks.
Type `cap -e taskname' to view it.
Amandil:~/capcidemo/trunk sintaks$
And Now, The Fun Part...
Since Capistrano was originally created to deploy Rails applications, most of the recipes it comes with are Rails-specific, so we'll be overriding a few of the default tasks.
set :application, "capcidemo"
set :repository, "http://labs.vazav.com/capcidemo/trunk"
set :user, "celericp"
set :domain, "apps.vazav.com"
set :scm_username, "deploy"
set(:scm_password){Capistrano::CLI.password_prompt("subversion password: ")}
set :deploy_to, "/home/#{user}/#{domain}/#{application}"
set :use_sudo, false
role :app, domain
namespace :deploy do
desc <<-DESC
# ...
DESC
task :finalize_update, :except => { :no_release => true } do
run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true)
# mkdir -p is making sure that the directories are there for some SCM's that don't
# save empty folders
run <<-CMD
rm -rf #{latest_release}/system/logs &&
mkdir -p #{latest_release}/htdocs &&
ln -s #{shared_path}/logs #{latest_release}/system/logs
CMD
stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
asset_paths = %w(images stylesheets javascripts).map { |p| "#{latest_release}/htdocs/#{p}" }.join(" ")
run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }
end
desc <<-DESC
# ...
DESC
task :restart, :roles => :app, :except => { :no_release => true } do
# For PHP apps, you don't need to restart the app server, so we'll No-Op this.
# invoke_command "#{current_path}/script/process/reaper", :via => run_method
end
end
(Note: There's a link to download this deploy.rb file at the end of this post. I've had to remove the descriptions here to get code formatting to work nicely.)
And we'll check in our changes:
Amandil:~/capcidemo/trunk sintaks$ svn status
M config/deploy.rb
Amandil:~/capcidemo/trunk sintaks$ svn ci -m "custom deploy tasks" config/deploy.rb
Sending config/deploy.rb
Transmitting file data .
Committed revision 3.
Amandil:~/capcidemo/trunk sintaks$
Some of this is a little heavy. Basically, we're overriding a few tasks to match our settings and situation. Namely, we're overriding :finalize_update to change some of the locations of certain things, like log files.
The :restart task is used in Rails to run a script that would restart our server. We don't have a need to restart our app server, since PHP's app server is actually the Apache web server (in our case), and no restart is required for Apache to load the new code.
You could write a task to restart Apache, but you'd probably want that to live in the web namespace, not the deploy one. I'm not going to go into details with that now.
Deploying To The Server
Before we can deploy, we need to ask Capistrano to create a few directories on the server. This is done using the deploy:setup task:
Amandil:~/capcidemo/trunk sintaks$ cap deploy:setup
* executing `deploy:setup'
* executing "umask 02 && mkdir -p /home/celericp/apps.vazav.com/capcidemo /home/celericp/apps.vazav.com/capcidemo/releases /home/celericp/apps.vazav.com/capcidemo/shared /home/celericp/apps.vazav.com/capcidemo/shared/system /home/celericp/apps.vazav.com/capcidemo/shared/log /home/celericp/apps.vazav.com/capcidemo/shared/pids"
servers: ["apps.vazav.com"]
Password:
[apps.vazav.com] executing command
command finished
Amandil:~/capcidemo/trunk sintaks$
It's actually creating a few directories we won't be using, like pids and system, but they're pretty harmless. If you're feeling particularly needy, you can override the default deploy:setup task to only create the directories you need. You can peek at the existing one by viewing the files in the Capistrano gem. On my system, the default deploy tasks are defined in /usr/local/lib/ruby/gems/1.8/gems/capistrano-2.2.0/lib/capistrano/recipes/deploy.rb. Note you don't want to actually change the files in this location, as they are shared across all capified projects. You can just override the ones you need by putting them in your trunk/config/deploy.rb file.
Minor Hitch!
Okay... when I got to this stage, I discovered that the current directory had reappeared on my server in /users/celericp/apps.vazav.com/capcidemo/, which, as I said earlier, is a bad thing. Turns out I had gone in an changed my DreamHost settings again, and those darn robots put the current directory back in there.
It's important to note that this issue can be avoided with more advanced knowledge of Capistrano. One could override additional tasks to do a forced removal of the current symlink/directory (or do one of a dozen other things). This issue is really just because DreamHost makes your document root directory if it doesn't exist. If you're manually configuring Apache, you could set your document root to [..]/current/htdocs without issue.
Alright. Moment of truth time:
Amandil:~/capcidemo/trunk sintaks$ cap deploy
* executing `deploy'
* executing `deploy:update'
** transaction: start
* executing `deploy:update_code'
subversion password:
* executing "svn checkout -q --username deploy --password sup3rs3cr3t --no-auth-cache -r3 http://labs.vazav.com/capcidemo/trunk /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252 && (echo 3 > /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/REVISION)"
servers: ["apps.vazav.com"]
Password:
[apps.vazav.com] executing command
command finished
* executing `deploy:finalize_update'
* executing "chmod -R g+w /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252"
servers: ["apps.vazav.com"]
[apps.vazav.com] executing command
command finished
* executing "rm -rf /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/system/logs &&\n mkdir -p /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/htdocs &&\n ln -s /home/celericp/apps.vazav.com/capcidemo/shared/logs /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/system/logs"
servers: ["apps.vazav.com"]
[apps.vazav.com] executing command
command finished
* executing "find /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/htdocs/images /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/htdocs/stylesheets /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/htdocs/javascripts -exec touch -t 200803270613.31 {} ';'; true"
servers: ["apps.vazav.com"]
[apps.vazav.com] executing command
*** [err :: apps.vazav.com] find: /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/htdocs/images
*** [err :: apps.vazav.com] : No such file or directory
*** [err :: apps.vazav.com] find: /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/htdocs/stylesheets
*** [err :: apps.vazav.com] : No such file or directory
*** [err :: apps.vazav.com] find: /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252/htdocs/javascripts
*** [err :: apps.vazav.com] : No such file or directory
command finished
* executing `deploy:symlink'
* executing "rm -f /home/celericp/apps.vazav.com/capcidemo/current && ln -s /home/celericp/apps.vazav.com/capcidemo/releases/20080327061252 /home/celericp/apps.vazav.com/capcidemo/current"
servers: ["apps.vazav.com"]
[apps.vazav.com] executing command
command finished
** transaction: commit
* executing `deploy:restart'
Amandil:~/capcidemo/trunk sintaks$
So far so good. Visit in the browser:

Success!
Let's Test The Magic
So, I said Capistrano made it easy to deploy updates to your site. After going through this whole mess, I bet you don't believe me. It's true! While the setup was a long (though straightforward) process, everything from here on is smooth-sailing.
Don't believe me? I'll show you.
Open trunk/system/application/controllers/welcome.php and make it look like this:
<?php
class Welcome extends Controller {
function Welcome()
{
parent::Controller();
}
function index()
{
$this->load->view('welcome_message');
}
function capistrano()
{
$this->load->view('capistrano');
}
}
?>
Then create trunk/system/application/views/capistrano.php and save the contents as:
Hello from Capistrano!
Add the new capistrano.php file to your repository and commit the changes:
Amandil:~/capcidemo/trunk sintaks$ svn status
M system/application/controllers/welcome.php
? system/application/views/capistrano.php
Amandil:~/capcidemo/trunk sintaks$ svn add system/application/views/capistrano.php
A system/application/views/capistrano.php
Amandil:~/capcidemo/trunk sintaks$ svn ci -m "capistrano says hello"
Sending system/application/controllers/welcome.php
Adding system/application/views/capistrano.php
Transmitting file data ...
Committed revision 5.
Amandil:~/capcidemo/trunk sintaks$
(Yes, I jumped a couple revisions adding a file I forgot...)
And deploy:
Amandil:~/capcidemo/trunk sintaks$ cap deploy
* executing `deploy'
* executing `deploy:update'
** transaction: start
* executing `deploy:update_code'
subversion password:
* executing "svn checkout -q --username deploy --password sup3rs3cr3t --no-auth-cache -r5 http://labs.vazav.com/capcidemo/trunk /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708 && (echo 5 > /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/REVISION)"
servers: ["apps.vazav.com"]
Password:
[apps.vazav.com] executing command
command finished
* executing `deploy:finalize_update'
* executing "chmod -R g+w /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708"
servers: ["apps.vazav.com"]
[apps.vazav.com] executing command
command finished
* executing "rm -rf /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/system/logs &&\n mkdir -p /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/htdocs &&\n ln -s /home/celericp/apps.vazav.com/capcidemo/shared/logs /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/system/logs"
servers: ["apps.vazav.com"]
[apps.vazav.com] executing command
command finished
* executing "find /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/htdocs/images /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/htdocs/stylesheets /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/htdocs/javascripts -exec touch -t 200803270647.49 {} ';'; true"
servers: ["apps.vazav.com"]
[apps.vazav.com] executing command
*** [err :: apps.vazav.com] find: /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/htdocs/images
*** [err :: apps.vazav.com] : No such file or directory
*** [err :: apps.vazav.com] find: /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/htdocs/stylesheets: No such file or directory
*** [err :: apps.vazav.com]
*** [err :: apps.vazav.com] find: /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708/htdocs/javascripts: No such file or directory
*** [err :: apps.vazav.com]
command finished
* executing `deploy:symlink'
* executing "rm -f /home/celericp/apps.vazav.com/capcidemo/current && ln -s /home/celericp/apps.vazav.com/capcidemo/releases/20080327064708 /home/celericp/apps.vazav.com/capcidemo/current"
servers: ["apps.vazav.com"]
[apps.vazav.com] executing command
command finished
** transaction: commit
* executing `deploy:restart'
Amandil:~/capcidemo/trunk sintaks$
Now visit http://apps.vazav.com/welcome/capistrano (substitute your URL in for mine):

Huzzah!
What Have We Learned?
Following this tutorial, you should be able to:
- Set up a CodeIgniter project
- Set up a hosted domain in DreamHost
- Set up a Subversion repository in DreamHost
- Use Capistrano to deploy your CodeIgniter projects
Remember, Capistrano can be used for a whooooole lot more than just deploying software. After you've learned a fair bit of Ruby and taken a look at the baked-in tasks, you should be able to override, extend, and create tasks to perform just about any remote operation. Additionally, by supplying multiple domains for each role (e.g., :app), you can have all your commands run on each server simultaneously.
Pretty friggin' sweet.
Resources
You can download the deploy file I used in this project here:
Just be sure to swap out my settings with yours, or you'll be scratching your head wondering why it's trying to deploy to my server. =P