Creating Symlinks When Deploying a Rails Application
One of the really cool parts of deploying a Rails application is using Capistrano to make it easy. I still sit and revel in the simplicity each time I deploy changes to a Rails application. However, I'll be the first to admit that the key to happy deployment lies in a good deployment recipe. Without one, deployment can be a frustrating and extremely annoying process. Case in point, have you ever deployed changes to your Rails application only to find that all the images uploaded through you website have disappeared, hidden away in a previous "version" of your application?
The Problem
We'll take a look at how to fix that problem by using the shared directory on your server and taking advantage of symbolic links, or symlinks. But first, let's explain why deploying with Capistrano can cause these problems.
When you use Capistrano to deploy your Rails application, your first go through the process of setting up your server, hopefully by using the cap deploy:setup task. Running this will cause Capistrano to create a couple of directories on your web server called current, releases and shared. The following explains how each of these directories is used:
The
currentdirectory is where the most current version of your Rails application is located. Each time you deploy your application, the contents of this directory are overwritten with your new changes.The
releasesdirectory stores previous releases of your application, which allow Capistrano to "roll back" changes in the event that something goes wrong. Each time you deploy your application, the contents of thecurrentdirectory are moved to thereleasesbefore the new version is copied to thecurrentdirectory. You will want to limit the number of previous releases that are stored to preserve disk space. This can be done by runningcap deploy:cleanup.The
shareddirectory contains assets that are independent of the version of your application. In other words, theshareddirectory is left alone during a deployment, which makes it a perfect place to hold uploaded files and documents that you want to preserve between migrations.
Symbolic Links
As you may have guessed, I'm going to discuss how to use symbolic links in your deployment recipe in order to preserve content between deployments. In our example a website allows people to upload images for display on the site. However, we want to avoid having these images stored in the current directory so that they are not moved to the releases when the Rails application is deployed.
First of all, let's assume that our images are being uploaded to a directory called uploads, located in the public directory of our Rails application. The first step in our process is to remove that directory from our repository (or set the Subversion ignore settings) to ensure that the directory is not being contained in our Rails application.
So now that we have removed that directory, we need somewhere to upload files to. In addition, our application is already set up to use the uploads directory and we shouldn't have to go through and change any of that. We will create an uploads directory on our web server inside of the shared directory, perhaps even inside the system directory just to keep things all together. Make sure to set the permissions so that files can be written to that directory by the application.
We now have a place for the files to be uploaded that will not be affected by deployments. The only thing that we need to do next is to use a symlink, or symbolic link, to tell the web server that links to the public/uploads directory inside the current Rails application should actually lead to the uploads directory inside the shared folder, which is not affected by deployments. It's like saying to the web server "treat this shared uploads folder as though it is the public uploads folder of the most current Rails app version."
We create this symbolic link by running the following command on our web server:
ln -nfs shared/system/uploads current/public/uploads
If you check your application, you will see that the uploaded image files are now being stored in the shared uploads directory. However, this will require us to log in to our web server after each deployment and create this symlink, which is annoying. Luckily, we can create a Capistrano deployment recipe that automates this task.
The Deployment Recipe
Rather than having to manually create our symlinks, let's take a look at how to create a custom deployment task that will be run whenever we deploy our Rails application.
This is a great time to mention that that
config/database.ymlfile is a wonderful candidate for storing in theshareddirectory and symlinking to it. By doing this, you can avoid having to publish your development and testing passwords, and multiple developers can easily collaborate on the same project.
Let's get started by opening up the config/deploy.rb file that contains our deployment recipe. Since our application is already up and running, we will only be adding some things to the end of the recipe that is currently in place for your web host. In this example we create a namespace called "customs" for our new tasks:
namespace(:customs) do
task :config, :roles => :app do
run <<-CMD
ln -nfs #{shared_path}/system/database.yml #{release_path}/config/database.yml
CMD
end
task :symlink, :roles => :app do
run <<-CMD
ln -nfs #{shared_path}/system/uploads #{release_path}/public/uploads
CMD
end
end
As you can see, we have defined two custom tasks, one called config and one called symlink. They both create symlinks, with the first creating a symlink for our database.yml file, and the second creating a symlink for our uploads directory. You might have noticed that Capistrano gives us a couple of variables to work with:
release_path is the path to the current release that is being deployed. In our case, this is the version of the Rails application in the
currentdirectory, but using this variable makes life easy on the coding side of things.shared_path is the path to the
shareddirectory of our application.
In order to get Capistrano to perform these tasks each time we deploy our application, we need to set some hooks by placing the following code at the bottom of our deploy.rb file:
after "deploy:update_code", "customs:config"
after "deploy:symlink","customs:symlink"
after "deploy", "deploy:cleanup"
The first line tells Capistrano that after it has performed the update_code methods, which updates the Rails application, that it should symlink to our shared database.yml file so that Rails has the production database configuration settings.
The next line tells Capistrano that once it has finished symlinking the application (a normal deploy involves symlinking to the current directory), it should then create the symlink for our uploads directory.
Finally, I have added a third line that tells Capistrano to finish it all up by running the cap deploy:cleanup task, which removes all but the last 5 releases from the releases directory. We do this for good housekeeping reasons so that we are not bloating our server with releases that we don't need.
And with that, we have successfully created symbolic links that will allow us to define parts of our application that are shared among all releases, and independent of the deployment process.
This post is filed under Developers' Corner and has the following keyword tags: ruby, rails, capistrano, deployment, symlink.