Deploying static sites with Capistrano
This article was originally published on 12/11/2008 by Stephan over at Brighternet. Migrated with permission.
Capistrano is a tool for automating tasks on remote servers via SSH. It simplifies the process of application deployment and maintenance by allowing you to wrap up a number of repetitive tasks into succinct Capistrano commands (called tasks).
Because Capistrano sprung from the Ruby on Rails community its default operation is designed for Rails applications. However, with a few small changes it can be used to deploy anything.
This article describes how to use Capistrano 2.5.1 to deploy non-Rails web sites, such as static HTML or PHP sites.
Steps
This guide consists of the following steps:
- What will we deploy?
- Install Capistrano
- Capistrano tasks
- Assumptions
- For the impatient
- Local setup
- Configure Capistrano
- Remote setup
- Deploy the site
- Rolling back to previous version
- References
What will we deploy?
For the purposes of this article, we will deploy a basic static website consisting of HTML, CSS, PHP files and images.
I have the following files for my test site:
- 2 HTML pages.
- 1 CSS files in a ‘css’ directory.
- 1 PHP file.
- 1 image in an ‘images’ directory.
Install Capistrano
Capistrano can be installed using the gem program, which you can get from the RubyGems website. You will need to have the Ruby Programming Language installed on your machine.
gem install capistrano
Capistrano tasks
Capistrano provides a number of tasks to aid application deployment. Once configured properly, these will do the following:
- deploy:setup – Set up the web server so that the site can be deployed.
- deply – Deploy the latest changes to the site.
- deploy:rollback – Roll back to the previous version.
There are other tasks. I’ll show you how to see a full list when you’ve completed the Local setup.
Assumptions
Capistrano can be configured to work in a variety of ways, using different mechanisms for deploying applications, but for the purposes of this article I’ll keep it simple with the following set of assumptions.
- You have SSH access to your web server.
- Your local machine and the web server have the usual Linux tools installed – ssh, tar, etc..
- You only have one remote server on which you keep your site. Capistrano defines three roles for running tasks on different types of remote location – web, app and db – because this is often how web applications separate resources. For the sake of simplicity, this article will set all roles to use the same remote location, the web server.
- You do not have to maintain your web site in Subversion (a version control system) on your local machine, but it is good practice. This guide explains how to use Capistrano both with and without Subversion.
- The web server does not need to have Subversion installed regardless of whether you use it on your local machine or not.
For the impatient
Here’s the full Capistrano deploy file used in this article: deploy.txt. It’s well commented, but you’re best off reading the rest of this article.
Local setup
Capistrano can deploy files from anywhere on your local system, but to keep things simple we’ll keep everything in the same place.
Create a directory called ‘mysite’, then create two subdirectories called ‘public’ – which will hold the static site files mentioned above – and ‘config’ – which will hold the Capistrano deploy file (note: the deploy file is where you configure Capistrano tasks).
steph@localhost ~ $ mkdir -p mysite/public mysite/config && cd mysite steph@localhost ~/mysite $ ls config public
Place your website files in ‘public’.
steph@localhost ~/mysite $ ls public/ css images index.html test.html test.php
If you are using Subversion, make sure that the public directory is under version control and up to date, because it will be checked out later when we deploy.
Generate the Capistrano configuration files using the ‘capify’ command. If you forgot to create the config directory you’ll have to create it and capify again. Don’t forget the dot!
steph@localhost ~/mysite $ capify . [add] writing `./Capfile' [add] writing `./config/deploy.rb' [done] capified!
You now have two new files, Capfile and config/deploy.rb. It doesn’t matter whether you add these to version control or not.
steph@localhost ~/mysite $ ls Capfile config public steph@localhost ~/mysite $ ls config/ deploy.rb
View all available Capistrano tasks
Now that you’ve capified mysite, you can view a list of all available Capistrano tasks.
cap -T
Configure Capistrano
The Capistrano “recipe” in config/deploy.rb is just a skeleton file which will need changing.
set :application, "set your application name here" set :repository, "set your repository location here" # 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/#{application}" # If you aren't using Subversion to manage your source code, specify # your SCM below: # set :scm, :subversion role :app, "your app-server here" role :web, "your web-server here" role :db, "your db-server here", :primary => true
Changes to the recipe
We edit config/deploy.rb to add the name of our application and details of the web server, including its location, user and SSH port. You don’t need to specify the port if it’s still the default 22 (though you should change the port number for security reasons).
We reference the location later on in the recipe, when we define the application, web and database server roles.
set :application, "mysite" set :location, "webserver" set :user, "gerald" set :port, 2896 ... role :app, location role :web, location role :db, location, :primary => true
We also tell Capistrano not to use the sudo command, otherwise it’ll attempt to run all remote commands through sudo. Don’t add this line (or set it to true) if you’d like to use sudo.
set :use_sudo, false
We create a new variable to hold the name of the local user, because we make use of it in other parts of the configuration.
set :local_user, "steph"
The deploy_to variable holds the directory on the web server that the site will be copied to. In this case, I have set the location to be a directory with the same name as the application (mysite) in the user’s home directory. So /home/gerald/mysite will be created on the web server.
The deploy_via variable tells Capistrano how we want to transfer the files to the remote server. Capistrano provides a number of different methods, but because we don’t have Subversion on the remote server, using “copy” is best. The files will be copied across as an archive rather than using Subversion on the remote machine.
set :deploy_to, "/home/#{user}/#{application}" set :deploy_via, :copy
The copy_dir and copy_remote_dir variables hold the directory where the temporary archive will be stored, for the local machine and web server respectively. For the example configuration below I set this to be tmp/capistrano in the user’s home directory. If the directory specified doesn’t exist then copying will fail, so two tasks called create_copy_dir and create_remote_copy_dir will be added to create them. We’ll get to those later.
You could leave out the copy_dir or copy_remote_dir variables and they’ll default to /tmp, but on my test machine /tmp is on a different partition to the normal filestore and deployment fails when Capistrano tries to create a hard link to it (which can’t be done across devices).
set :copy_dir, "/home/#{local_user}/tmp/capistrano" set :copy_remote_dir, "/home/#{user}/tmp/capistrano"
Create custom tasks
We are going to create custom tasks to aid our deployment. To keep things tidy, we put these tasks into their own namespaces. We can then call a task using the following semantics:
namespace:task
So the deploy:setup task I mentioned earlier is actually a task called ‘setup’ in the namespace ‘deploy’. When a namespace is called without a task name, a task called ‘default’ is called. Hence, deploy is short for deploy:default.
To create the copy_dir and remote_copy_dir, we need two custom tasks. We first create two namespaces, called ‘remote’ and ‘local’, in which we put custom tasks that make changes to the local machine and the web server respectively. We add a callback at the end of the recipe to trigger the tasks before deploy:setup, because copy_dir and remote_copy_dir are needed before we deploy.
# Custom tasks for our hosting environment. namespace :remote do desc <<-DESC Create directory required by copy_remote_dir. DESC task :create_copy_remote_dir, :roles => :app do print " creating #{copy_remote_dir}.\n" run "mkdir -p #{copy_remote_dir}" end end # Custom tasks for our local machine. namespace :local do desc <<-DESC Create directory required by copy_dir. DESC task :create_copy_dir do print " creating #{copy_dir}.\n" system "mkdir -p #{copy_dir}" end end # Callbacks. before 'deploy:setup', 'local:create_copy_dir', 'remote:create_copy_remote_dir'
Override default tasks
We override some tasks that only apply to Rails applications – migrate, finalize_update, start, stop and restart – so that we don’t get any error messages.
# Override default tasks which are not relevant to a non-rails app. namespace :deploy do task :migrate do puts " not doing migrate because not a Rails application." end task :finalize_update do puts " not doing finalize_update because not a Rails application." end task :start do puts " not doing start because not a Rails application." end task :stop do puts " not doing stop because not a Rails application." end task :restart do puts " not doing restart because not a Rails application." end end
We now need to tell Capistrano where to find the files on your local machine – configuration which differs depending on whether we’re using Subversion or not.
Use with Subversion
The repository variable holds the location of the repository. It can be any valid Subversion URL regardless of protocol (file, svn, svn+ssh, http, etc..). We set the location to be that of the public directory, so that only the contents of the static site is uploaded.
set :repository, "file:///home/#{local_user}/repositories/#{application}/public"
With the copy strategy, Capistrano will checkout the code into a local cache directory, tar it into an archive, copy the archive to the web server, untar it and move it to become the latest release. Every time a deploy is done, the local cache is updated and the archive copied across as before. Because we’re updating the cache rather than checking out (or exporting) from scratch, it speeds up deployment time.
We set the copy_cache variable to tell Capistrano where to check out the code on the local machine. As mentioned above, the copy_dir variable tells it where to store the temporary archive. In my example configuration below, I set copy_cache using other variables so that it’s a subdirectory of copy_dir with the same name as the application.
Note that a) the copy_dir directory must not be at the same as (nor inside) the copy_cache directory and b) the copy_cache directory must not exist before deploy:cold (if you are using it – more on that later) to ensure that a checkout is done instead of an update.
set :copy_cache, "#{copy_dir}/#{application}"
We also tell Capistrano not to copy across any .svn directories, using the copy_exclude variable, because they contain potentially sensitive information that we don’t want available to the world.
set :copy_exclude, [".svn", "**/.svn"]
Use without Subversion
We set the repository variable to point to the public directory and tell Capistrano that we’re not using version control. Capistrano will tar the files into an archive, copy it to the web server, untar it and move it to become the latest release.
set :repository, "./public" set :scm, :none
Remote setup
To create the required files and directories on your web server, we run the ‘deploy:setup’ task. However, before doing this we will add another custom task so that the site we will deploy can be seen by the web server (e.g. by Apache).
Configure the web server to see the site
Because we will deploy to a subdirectory of the user’s home directory, the site will probably not be visible to the web server. The configuration necessary largely depends on the web server you’re using. If you’re using Apache configured with the USER_DIR option – so that it can see files in the user’s public_html directory – then simply create a symbolic (AKA soft) link to ~/mysite/current.
gerald@webserver ~ $ ln -s ~/mysite/current ~/public_html/mysite gerald@webserver ~ $ ls -l public_html/ total 0 lrwxrwxrwx 1 gerald users 17 Nov 12 17:21 mysite -> /home/gerald/mysite/current
You should now be able to view the site at http://webserver/~gerald/mysite.
If you’re in shared hosting then you could have created an “addon” domain, which in turn would have created a directory in public_html with the same name as the domain. Make sure it’s empty, then delete it and create a symbolic link with the same name that points to ~/public_html/mysite.
Note that you will need Apache configured to follow symbolic links. Place the following in your .htaccess file:
Options +FollowSymLinks
Custom task
We can of course use Capistrano to automatically create the link. We do this by creating a custom task that is run after deploy:setup. We’ll add the task to the ‘remote’ namespace we created earlier. Note that earlier versions of Capistrano recommended the use of a task called ‘deploy:cold’, which was used to deploy the application for the first time. This is now discouraged, so we won’t be using it.
namespace :remote do ... desc <<-DESC Create a symlink to the application. DESC task :create_symlink, :roles => :web do print " creating symlink ~/public_html/#{application} -> #{current_path}.\n" run "ln -s #{current_path} ~/public_html/#{application}" end end ... # Callbacks. after 'deploy:setup', 'remote:create_symlink'
Note the use of the ‘current_path’ variable. This is an in built variable that points to the location of the current release, which in this example will be ~/mysite/current (it hasn’t been created yet because we haven’t deployed the site yet).
You could also create a Capistrano task to configure .htaccess, but for the sake of simplicity I won’t write one for this article.
Set up the web server for Capistrano
Now we can run ‘deploy:setup’ to configure the web server for Capistrano.
cap deploy:setup
You will be prompted for the ssh password (unless you have configured your machines to use PGP keys, which is recommended for security and convenience).
steph@localhost ~/mysite $ cap deploy:setup
* executing `deploy:setup'
triggering before callbacks for `deploy:setup'
* executing `local:create_copy_dir'
creating /home/steph/tmp/capistrano.
* executing `remote:create_copy_remote_dir'
creating /home/gerald/tmp/capistrano.
* executing "mkdir -p /home/gerald/tmp/capistrano"
servers: ["webserver"]
[webserver] executing command
command finished
* executing "mkdir -p /home/gerald/mysite /home/gerald/mysite/releases /home/gerald/mysite/shared /home/gerald/mysite/shared/system /home/gerald/mysite/shared/log /home/gerald/mysite/shared/pids && chmod g+w /home/gerald/mysite /home/gerald/mysite/releases /home/gerald/mysite/shared /home/gerald/mysite/shared/system /home/gerald/mysite/shared/log /home/gerald/mysite/shared/pids"
servers: ["webserver"]
[webserver] executing command
command finished
triggering after callbacks for `deploy:setup'
* executing `remote:create_symlink'
creating symlink ~/public_html/mysite -> /home/gerald/mysite/current. Configure your server to use this link.
* executing "ln -s /home/gerald/mysite/current ~/public_html/mysite"
servers: ["webserver"]
[webserver] executing command
command finishedLook on the server and you should see that /home/gerald/tmp/capistrano and /home/gerald/mysite has been created.
gerald@webserver ~ $ ls tmp/ capistrano gerald@webserver ~ $ ls mysite/ releases shared
The releases directory will keep copies of each version of the site as you deploy them. The shared directory is used to hold various shared files used by Rails applications, hence we can ignore it. We didn’t bother overriding the setup task to prevent them being created because it’s too complicated for the purposes of this article.
Deploy the site
Now that Capistrano has been set up, we can deploy the site using the ‘deploy’ task.
cap deploy
If you’re using Subversion on your local machine, make sure the repository is up to date, because Capistrano will check out the latest revision to deploy.
The example below shows the output when using Subversion.
steph@localhost ~/mysite $ cap deploy
* executing `deploy'
* executing `deploy:update'
** transaction: start
* executing `deploy:update_code'
* preparing local cache at /home/steph/tmp/capistrano/mysite
executing locally: "svn info file:///home/steph/repositories/experiments/capistrano/static/public -rHEAD"
executing locally: svn checkout -q -r217 file:///home/steph/repositories/experiments/capistrano/static/public /home/steph/tmp/capistrano/mysite
* copying cache to deployment staging area /home/steph/tmp/capistrano/20081112162016
compressing /home/steph/tmp/capistrano/20081112162016 to /home/steph/tmp/capistrano/20081112162016.tar.gz
executing locally: tar czf 20081112162016.tar.gz 20081112162016
servers: ["webserver"]
** sftp upload /home/steph/tmp/capistrano/20081112162016.tar.gz -> /home/gerald/tmp/capistrano/20081112162016.tar.gz
[webserver] /home/gerald/tmp/capistrano/20081112162016.tar.gz
[webserver] done
* sftp upload complete
* executing "cd /home/gerald/mysite/releases && tar xzf /home/gerald/tmp/capistrano/20081112162016.tar.gz && rm /home/gerald/tmp/capistrano/20081112162016.tar.gz"
servers: ["webserver"]
[webserver] executing command
command finished
* executing `deploy:finalize_update'
not doing finalize_update because not a Rails application.
* executing `deploy:symlink'
* executing "rm -f /home/gerald/mysite/current && ln -s /home/gerald/mysite/releases/20081112162016 /home/gerald/mysite/current"
servers: ["webserver"]
[webserver] executing command
command finished
** transaction: commit
* executing `deploy:restart'
not doing restart because not a Rails application.Capistrano has now copied across your site into the releases directory on the web server and created a link to it called ‘current’. Every time you deploy from now on, a new release directory will be created and the current link will be updated to point to the most recent release.
gerald@webserver ~ $ ls -l mysite/ total 0 lrwxrwxrwx 1 gerald users 42 Nov 12 16:20 current -> /home/gerald/mysite/releases/20081112162016 drwxrwxr-x 3 gerald users 80 Nov 12 16:20 releases drwxrwxr-x 5 gerald users 120 Nov 12 16:20 shared gerald@webserver ~ $ ls -l mysite/current/ total 16 -rw-r--r-- 1 gerald users 4 Nov 12 16:20 REVISION drwxr-xr-x 2 gerald users 80 Nov 12 16:20 css drwxr-xr-x 2 gerald users 88 Nov 12 16:20 images -rw-r--r-- 1 gerald users 809 Nov 12 16:20 index.html -rw-r--r-- 1 gerald users 817 Nov 12 16:20 test.html -rw-r--r-- 1 gerald users 855 Nov 12 16:20 test.php
Every time you change the site on your local machine, run the deploy task to deploy the changes. On the web server, you’ll see that a new release directory has been created and the target of the ‘current’ link has changed to point to it.
gerald@webserver ~ $ ls -l mysite/ total 0 lrwxrwxrwx 1 gerald users 42 Nov 12 17:54 current -> /home/gerald/mysite/releases/20081112164358 drwxrwxr-x 4 gerald users 112 Nov 12 17:54 releases drwxrwxr-x 5 gerald users 120 Nov 12 17:20 shared
Rolling back to previous version
If you make a bad change to the site and you want to roll back to the previous version, run the ‘deploy:rollback’ task. The ‘current’ link will be updated to point to the previous release and the bad release will be deleted.
cap deploy:rollback
steph@localhost ~/mysite $ cap deploy:rollback
* executing `deploy:rollback'
* executing `deploy:rollback:revision'
* executing "ls -xt /home/gerald/mysite/releases"
servers: ["webserver"]
[webserver] executing command
command finished
* executing "rm /home/gerald/mysite/current; ln -s /home/gerald/mysite/releases/20081112162016 /home/gerald/mysite/current"
servers: ["webserver"]
[webserver] executing command
command finished
* executing `deploy:restart'
not doing restart because not a Rails application.
* executing `deploy:rollback:cleanup'
* executing "if [ `readlink /home/gerald/mysite/current` != /home/gerald/mysite/releases/20081112164358 ]; then rm -rf /home/gerald/mysite/releases/20081112164358; fi"
servers: ["webserver"]
[webserver] executing command
command finishedOn the server, you’ll see that the ‘current’ link now points to the previous release.
gerald@webserver ~ $ ls -l mysite/ total 0 lrwxrwxrwx 1 gerald users 42 Nov 12 17:55 current -> /home/gerald/mysite/releases/20081112162016 drwxrwxr-x 3 gerald users 80 Nov 12 17:55 releases drwxrwxr-x 5 gerald users 120 Nov 12 17:20 shared
References
- Capistrano: Home
- the { buckblogs :here }: Capistrano 2.3
- “(weblink)Simplistic Complexity >> Automated PHP Deployment With Capistrano(Simplistic Complexity >> Automated PHP Deployment With Capistrano)”:http://www.simplisticcomplexity.com/2006/8/16/automated-php-deployment-with-capistrano/
- /tools/capistrano – Rails Trac – Trac
Posted by Stephan on 18 Jun 2010. Tagged rails, php, capistrano.