When I learned Ruby on Rails, I found out about Capistrano. This whole automated deployment thing seemed pretty neat. But then my Rails project finished, and I was back to working on a PHP site. There I was, coding along in naive bliss.
Then I started working on voteforchange.com for the Obama Campaign. Nick and I didn’t have the biggest iron or the most … process. But we did have Capistrano for PHP.
A little light bulb went off in my head.
Heather’s sites were a great candidate for Capistrano deployments. I could stick everything in subversion … develop on my PowerBookMacBook Pro and deploy to the DreamHost web server. Professional deployment techniques for a site with an engineering team of one.
One thing was bothering me, though. The VFC Capistrano script was so much longer than the ones I was using for my Rails site. Longer. More complicated. Less elegant.
We were making our own folder structure:
t = Time.now
folder = "#{t.year}"+"#{t.month}"+"#{t.day}"+"#{t.hour}"+"#{t.min}"+"#{t.sec}"
Wasn’t Capistrano supposed to do that for me? Yes, it was. So I started looking around. Everyone was pointing at Jon Maddox’s instructions. Those were a good starting point, but I had to make a few changes. Here’s what I learned.
How to deploy PHP sites with Capistrano 2
- Cache your authentication credentials
- Configure Capistrano
- Run Capistrano’s initial setup
- deploy
Cache your authentication credentials
You’re going to want to set up passwordless authentication for both SSH and Subversion. Otherwise, you’ll get prompted every time you check in or deploy. (This also makes using TextMate‘s subversion bundle harder.)
- Cache your SSH credentials. Generate your keypair. On your local machine, do this:
ssh-keygen -t rsa
chmod -R 700 ~/.ssh
Repeat this on the web and svn servers.
-
Upload your public key from your local machine to the web (svn) server:
ssh www.mysite.com "echo '`cat $HOME/.ssh/id_rsa.pub`' >> .ssh/authorized_keys"
Do the same thing from the web server to the svn server.
-
Often, your username on your local machine is not the same as your username on the subversion server. To tell subversion what name to use, edit
~/.ssh/config
, adding lines like these:
Host svn.mysite.com
User joeblow
Host www.mysite.com
User joeblow
Test this out by typing ssh www.mysite.com
. If you still get prompted for a password, make sure the permissions are correct on the webserver (chmod -R 700 ~/.ssh
).
- On the web server, cache the subversion credentials. Just check out the repository:
svn co svn+ssh://svn.mysite.com/svn/mysite/trunk
Enter your password when prompted. Subversion will cache this information in ~/.subversion/auth/
. You can now delete the trunk directory.
Configure Capistrano
Normally, you prepare a project for Capistrano by typing capify
. You don’t really need to do that for PHP sites, because you don’t need a two-part Capistrano script. Instead, just create a text file called Capfile
that looks like this:
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
set :use_sudo, false
set :home_path, "/var/www/mysitename"
set :checkout, "export"
set :repository, "svn+ssh://svn.mysite.com/svn/mysite/trunk"
role :web, "www.mysite.com"
namespace :deploy do
task :restart do
end
end
Run Capistrano’s initial setup
Run the Capistrano setup command: cap setup
. This creates the releases/
and shared/
directories on the server.
Deploy
To get the site up, just run cap deploy
.
Tips, tricks and notes
- Following DreamHost’s Capistrano instructions, I set
use_sudo
to false. On a shared hosting site, I didn’t have root. Duh. Why is this the default option on Capistrano? Dunno.
- You need to disable Capistrano’s restart task. Since we aren’t using Rails, there was no mongrel, and nothing to restart.
So I tried this:
task :restart do
end
I still got an error. It turns out you have to specify the Capistrano namespace:
namespace :deploy do
task :restart do
#nothing
end
end
-
What about database passwords for your web site? You don’t want to store those in the subversion repository. As the Cap FAQ explains, stick a configuration file elsewhere on the server, and copy in a post-deploy task.
task :copy_database_configuration do
run "cp #{home_path}/config/#{config_file} #{release_path}/helpers/config.php"
end
after "deploy:update_code", :copy_database_configuration
-
If you’re developing with other people, having your SSH script prompt for your SSH username can be helpful:
set(:user) { Capistrano::CLI.ui.ask("SSH username: ") }
-
You can configure your script to do different work on staging versus production deployments. There are probably better ways to do this, but here’s what I have working for me. Suppose you have staging and production on the same server:
if ENV['PRODUCTION']
set :home_path, "/var/www/production"
else
set :home_path, "/var/www/stage"
end
By default, cap deploy
deploys to stage. To deploy to production, run PRODUCTION=1 cap deploy
(assuming bash).
-
If you’re deploying WordPress, you will want to make sure the uploads directory is writeable. You might also want a special .htaccess file put in place (I use different ones for localhost and production). Here’s how to do this:
task :after_symlink do
run "cp #{current_path}/config/show_htaccess #{current_path}/.htaccess"
run "chmod -R a+w #{current_path}/wp-content/uploads/"
end
Of course, your subversion deployment will overwrite your existing uploads directory, so this particular example is of limited value.
More details
Clayton Lengel-Zigich’s tips for using Capistrano with PHP and the Capistrano FAQ are very helpful.