PHP Deployment With Capistrano

If you’ve ever used Capistrano to deploy your Rails application you know how simple the process is. With a little configuration you have a respectable deployment strategy that integrates nicely with your source control system and deployment server. If you’re stuck writing your apps in PHP you might think that you are restricted to using rsync, scp or plain old FTP. Fear not! Getting Capistrano deploying your PHP apps is a lot easier than you might think.

Assumptions & Requirements

There are assumptions that I will be making throughout this article, they include:

  • You are using OS X/Linux
  • You are comfortable using the command line
  • The code for your PHP application resides in some type of source control system
  • The deployment server is running some type of *nix
  • The deployment server is using a LAMP setup
  • You have Ruby and Ruby Gems installed on your system

Installing Capistrano

I’m using the latest (as of this post) version which is 2.4.3, it’s a typical gem install with:

	gem install -y Capistrano
	...
	Successfully installed Capistrano-2.4.3
	1 gem installed
	Installing ri documentation for Capistrano-2.4.3...
	Installing RDoc documentation for Capistrano-2.4.3...

Once you’ve got Capistrano installed we can move on to configuring our application.

Application Configuration

For this example I will be deploying a simple Hello World Application that I wrote in PHP. The code is stored in my subversion repository and I am deploying to my SliceHost Slice running Ubuntu Linux.

Step 1 – Create config Directory and Capify

Capistrano places its configuration file, called a recipe, in a directory named config. Since my application doesn’t have a config directory, I will create one.

clayton$ mkdir config

Next we will “Capify” the application. This creates the necessary default files.

	clayton$ capify .
	[add] writing `./Capfile'
	[add] writing `./config/deploy.rb'
	[done] capified!

We’ve got our default recipe in place and our application has been capified!

Step 2 – Modifying The Default Recipe (Configuration)

The default recipe is pretty simple and well(enough) documented. However, we do need to make some changes.

	# Application Naming
	#
	set :application, "set your application name here"
	set :repository,  "set your repository location here"

*:application: This is the name of the directory that will be created to hold the files related to your application. I will name mine “app”. It’s important to note that this probably *should not be your website’s DocumentRoot such as httpdocs or www.

*:repository*: This is the location of your source control repository. I’m using subversion so I’ll enter the URL for my repository.

	# Application Deployment Location
	#
	# If you aren't deploying to /u/apps/#{application} on the target
	# servers (which is the default), you can specify the actual location
	# via the :deploy_to variable:
	set :deploy_to, "/var/www/vhosts/hellowworld.tld/#{application}"
	set :document_root, "/var/www/vhosts/hellowworld.tld/httpdocs/current"

This bit of commented code is important. Chances are this is not how your server is configured and you will need to change the :deploy_to variable. You should set this to something reasonable, on my SliceHost server all of my sites are stored in /var/www/vhosts/_domain_/ so I’ll set my :deploy_to to something like /var/www/vhosts/hellowworld.tld.

*:document_root*: This isn’t a built in Capistrano variable, but I like to use to so that I can reference the DocumentRoot from my Apache VirtualHost later on in the deployment process.

Note: The previously set :application variable is appended to the end of your :deploy_to path so don’t put your application name in the :deploy_to variable.

	# Source Control Settings
	#
	set :scm_username, "cap" #if http
	set :scm_password, "fHw1n71d" #if http
	set :scm_checkout, "export"

If you’re using http to access your subversion repository you’ll need to add the username and password for a user who has httpauth access.

*:scm_username*: Provide the username of a user who has access to your subversion repository.

*:scm_password*: Provide the above user’s e-mail address.

*:scm_checkout*: Tell Capistrano what type of checkout to preform, we’ll use export.

Capistrano uses SSH to access your deployment server and run the commands required to deploy your application. You’ll need to tell Capistrano how to access your server.

	# SSH Settings
	#
	set :user, "clz"
	set :password, "password"
	set :use_sudo, false
	set :ssh_options, {:forward_agent => true}

*:user*: Set this to a user who has access to the :deploy_to directory.

*:password*: The above user’s password

*:use_sudo*: If the above user has sudo priviledges you can set this to true. I do not use it because of the way my server is configured. You might need to.

*:ssh_options*: This variable accepts a ruby hash, for valid options see Net-SSH. I’m using agent forwarding because I want to use my SSH key and don’t want to be prompted for a password. For more on agent forwarding see UnixWiz’s Illustrated Guide to SSH Agent Forwarding.

	# Server Roles
	#
	role :app, "your app-server here"
	role :web, "your web-server here"
	role :db,  "your db-server here", :primary => true

When deploying a Rails application it’s possible that you’ll have more than one application server and a database on another server, but with PHP chances are you’re running a LAMP setup and have Apache, PHP and MySQL all side-by-side. In my case I’m going to set all of these values to my server’s IP address.

Step 3 – Configuring For a PHP Deployment

If you were to try and deploy your application right now you’d run into a number of errors. Remember that Capistrano assumes you’re deploying a Rails application and because of that it will try and do some things that you don’t want it to do, restarting servers, creating Rails specific symbolic links etc. To get around these assumptions we will need to bypass or modify some of the existing default tasks.

	namespace :deploy do
 
		task :update do
			transaction do
				update_code
				symlink
			end
		end
 
		task :finalize_update do
			transaction do
				run "chmod -R g+w #{releases_path}/#{release_name}"
			end
		end
 
		task :symlink do
			transaction do
				run "ln -nfs #{current_release} #{deploy_to}/#{current_dir}"
				run "ln -nfs #{deploy_to}/#{current_dir} #{document_root}"
			end
		end
 
		task :migrate do
			# nothing
		end
 
		task :restart do
			# nothing
		end
 
	end

Update Task: This is a built in task in the deploy namespace. This is setup to do a few things, among them checkout your code from the repository and restart your server. However, we don’t need the server restart so we’ll override it and have it update the code and create symbolic links

Finalize Update: This was a step that I kept getting stuck on when first trying this out. I did not have this task in my deployment namespace and my file permissions were all goof’ed up. I found this via the Capistrano GitHub recipe.rb page.

Symlink: Since we’re not deploying directly to our site’s DocumentRoot we will need to tell Capistrano to create some symbolic links. When Capistrano updates the code, it checks out the lastest version of the code from your source control system and sticks it into a directory under deploy_to/releases inside of a directory named with the current timestamp. Usually Capistrano will create the a symbolic link named current that is pointing to the most recent release in deploy_to/releases, however, we’ll need to create that on our own.

*#{current_release}*: This is a default Capistrano variable for the most recent directory in deploy_to/releases.

*#{current_dir}*: This is a default Capistrano variable for the name of the “current” directory, by default it is current.

Migrate: Migrations are a Rails only thing so we’ll skip them and tell Capistrano to do nothing.

Restart: Capistrano will try to restart your Rails process or mongrels etc. Needless to say, with PHP this isn’t required. Technically you could throw a call to apache2ctl if you needed to.

Transactions: Recent versions of Capistrano allow you to specify transactions. If you encounter any errors while inside of a transaction all of the changes will be rolled back and your application won’t sit there on the internet broken.

Step 4 – The Initial “Setup” Deploy

The first step of deploying your PHP application with Capistrano is to run the setup task. This will create some directories on your deployment server in the correct location.

	clayton$ cap deploy:setup

Here’s what I got on my deployment server:

	/var/www/vhosts/helloworld.tld/app/shared
		/var/www/vhosts/helloworld.tld/app/shared/log
		/var/www/vhosts/helloworld.tld/app/shared/tmp
		/var/www/vhosts/helloworld.tld/app/shared/pid
	/var/www/vhosts/helloworld.tld/app/releases

You can get rid of the log, tmp and pid directories. Those are Rails specific and we won’t be using them.

Your application is now all ready to be deployed by Capistrano. You’ve even got a place to store some of your non-code related things like images and other files.

Step 5 – Making Use of Capistrano’s Shared Directories

My Hello World application has some images that are displayed on the page. However, I’m not storing my images in my source control system and thus, they are not automatically uploaded when I deploy my app with Capistrano. Luckily we can make use of Capistrano’s ability to run various commands on the deployment server to create some symbolic links to our images.

First, upload your images to a directory (call it whatever you want) under deploy_to/_application_/shared/. Mine is located at /var/www/vhosts/helloworld.tld/app/shared/images. Now we can update our recipe to create these symbolic links.

 
	# namespace :deploy... is up above
 
	task :after_symlink do
		transaction do
			run "ln -nsf #{shared_path}/images #{document_root}/images"
		end
	end

After Symlink: We want Capistrano to create our symbolic links after it has already linked up the document root and current release directories. Using run we tell Capistrano to execute the command on the deployment server. In this case we want to create a symbolic link in our document root that points to the shared images path.

*:shared_path_*: This is the default location and name of the “shared” directory created by our previous setup command.

By using symbolic links to link up our static non-code-related images, we avoid having to re-upload the images each time we deploy or worry about accidentally wiping out any files.

Step 6 – Deploying our PHP Application For the First Time

Typically with a Rails application you would run the following to deploy your application for the first time.

	clayton$ cap deploy:cold

This would checkout the code, rune the migrations and start your server. Since we don’t need those last two steps, we can just skip deploying “cold” and use the regular old deploy task.

	clayton$ cap deploy

At this point you’ll see a bunch of information running across your terminal window. If there are any errors you will see an error message from capistrano about a failed command on the deployment server or bad password. If everything went well your application will now be deployed on your server!

Future Deployments

Now that I’ve made some changes to my HelloWorld application I’m ready to deploy again. Capistrano makes this very easy, as long as I follow a simple process.

  1. Commit my changes to my source control system
  2. Upload any new static content (images, files etc.) to the shared directory
  3. Run cap deploy from my command line

You’ll again see the progress of Capistrano deploying the updated application run accross your terminal window and your new code will be deployed.

There’s A Lot to Learn About Capistrano

Capistrano is a great application, one of the best things about it is its simlicity and easy of use. However, it can also be pretty complex when you start digging into how it’s working behind the scenes. With that said, I encourage you to do some digging of your own and learn more about the default variables and tasks. There is probably quite a bit I could do above and beyond the defaults to make this PHP deployment strategy much better, but for now it works and my PHP deployments are easier than ever.

To learn more about Capistrano’s deployment recipes read the code and comments.

Final Deploy Recipe

View the full example deployment recipe