Other Lessons

Quicktime Video

Right-click on the button above and choose Save As to save file to your computer


Support the Course

There's no charge for the course, but we greatly appreciate any donations.

Suggested donations:
  • One or two lessons: $5
  • Several lessons $10
  • Entire course: $25 – $50

We hope you've found the course to be valuable, and we appreciate any support you care to provide.


Sign Up Now!

If you aren’t already receiving our course lessons via email, sign up now to be sure you don’t miss anything.

Every few days, we’ll send you an email with a link to the next episode, plus a list of additional resources for advancing your knowledge.

There’s no cost and no obligation. And we’ll never share your email address with any third party.

We’ll send you the first lesson right away.

Looking for a Powerful Hosted CMS?

The authors of the Learning Rails course also offer a very powerful hosted content management system for web designers, which enables you to build sophisticated, database-driven sites without programming. This is a great alternative to building a custom Ruby on Rails site for those applications for which you just can't justify the cost of a custom solution.

To learn more, sign up for the free Learning Webvanta course on building database-driven web sites without programming.

Lesson 22
Deploying to a Public Web Server

comments Bookmark and Share

Goals

In this lesson, we finally deploy our application to a public web server. Please note that, while we’ve tried to make these notes complete, they aren’t the full tutorial; that’s in the screencast, which you can access via the button on the left. We recommend that you right-click on the button and choose Save As to download it to your computer.

The code used in this lesson has all been checked in to the Github repository.

Rails deployment overview

Deploying a Rails application is more complex than for a simple web site or PHP application for two reasons:

  • In general, a separate application server is used to keep the Rails application in memory, and execute requests that come in through the HTTP server. This requires more configuration, and that the application server be restarted when the code is changed.
  • Static and PHP sites are typically deployed by simply uploading files to the server, with FTP or SFTP. Although there is nothing about Rails that requires it, Rails applications are almost universally deployed by having the server check the code out from a version control system. (This is a better practice for all kinds of sites, but it less often used with simpler technologies.)

The flow of a deployment is thus as follows:

  1. Make changes to your code locally
  2. Test the changes
  3. Check the changes in to version control (and push to the main repository, if using Git)
  4. Optionally, display a maintenance page to replace the site while it is being updated
  5. Perform a checkout of the code from the version control system to the web server
  6. Run any migrations that the new code requires
  7. Restart the application server
  8. Disable the maintenance page, if any

Although this may sound complicated, with the right tools it is quite simple once you’re all set up. The key tool is a program called Capistrano, which can automate the entire process (starting with step 4). In the previous few lessons, you’ve been through the first three steps; by the end of this lesson, you’ll have been through the whole process.

Choosing a web and application server configuration

There’s a huge array of choices to be made when it comes to server software selection and configuration. We’ll describe one of many possible approaches in this lesson. You’ll almost surely want to explore other options, and need to learn more about issues specific to your server, to do your own deployments. Here’s where to find some useful resources on our BuildingWebApps site:

We also highly recommend the book Deploying Rails Applications.

For the past two years or so, the most widely used setup has been Apache 2.2 as the front-end server, which processes HTTP requests, serves static files (including JavaScript, CSS, and images, as well as any cached HTML pages) and passes (“proxies”) requests that must handled by Rails to the application server(s). Many people have started using Nginx instead of Apache as the front-end server.

Whether Apache or Nginx is the HTTP server, the most widely used application server is Mongrel, which is installed as a gem. Mongrel handles only one request at a time, so most servers use two or more Mongrels (dozens for a system designed to handle high loads), and another gem called mongrel_cluster manages the Mongrels.

Setting this up can be a little complex, and if you’re going to take this route, going with a hosting company that provides a “stock” configuration, and knows how to support it, is usually a good investment.

A relatively new alternative, Passenger (a.k.a. mod_rails), makes all this much simpler by operating as an Apache module. Not only does it eliminate most of the configuration, it shuts down Rails processes that have been idle for a while, freeing the memory they use. This has the disadvantage of a several-second delay the first time an idle site is accessed, but it is a tremendous advantage for a server that is running lots of low-traffic sites, since they don’t always need to sit in memory.

In this lesson, we’ll use Apache 2.2 with Passenger.

Set up database configuration for MySQL

Before we get to the server setup, we need to do a little work on our application.

So far, we’ve used SQLite for our development and test databases, and the production database is set up to use it as well. MySQL is generally a better choice for production, so let’s change the production configuration.

In config/database.yml, replace the section under production to the following:

adapter: mysql
database: learningrails_production
username: deploy
password: secret
host: localhost

The development and test databases can remain set up for SQLite.

We’ve also added a migration) that will create all the pages and a couple of sample resource categories and links, so the application when deployed should look much like the version you have on your development system. This migration won’t do anything unless there are no pages defined in the database.

Remember that the contents of your local SQLite database aren’t transferred to production, so you’ll need to enter, on the production system, any data you’ve added to the database on your development system (or add a migration to do so, as we’ve done for the basic site contents). This is a bit of a pain for ongoing development — you’ll probably find that you’ll want to copy the database from the production system down to development when you’re doing development work, and sometimes push it back up to production when you’ve added content locally. A GUI tool such as Navicat simplifies this process.

Install Capistrano

Capistrano is a powerful utility for automating any kind of web deployment. It is nearly universally used by Rails developers.

First, install the Capistrano gem if you don’t already have it:

sudo gem install capistrano

We’ve used version 2.5 in this lesson. Some details will be different if you’re using an earlier version, and if it is much earlier, it may not work with Git.

Capistrano runs on your development system, not on the server. It performs actions on the server by connecting to it via SSH and issuing commands, just as you would from an SSH terminal session.

To configure the application for Capistrano, the first step is to “Capify” it. From your root application directory, simple enter in a terminal:

capify .

(That’s “capify”, space, period.)

This adds two files to your application: Capfile at the root of the app, and deploy.rb in the config directory. We don’t need to make any changes to Capfile.

Set up deploy.rb

The file deploy.rb is where you tell Capistrano how to access your code repository, as well as other details of your production server(s). The art of configuring Capistrano is mostly a matter of setting up the deploy.rb file.

There’s a guide at github that gives us some guidance about what is needed so Capistrano can fetch our code directly from our github repository. There’s also a lot of tutorial content at capify.org. Here’s the lines we need to add to support checking out from our public Git repository:

  default_run_options[:pty] = true
  set :repository,  "git://github.com/chaupt/learning-rails-sample-app.git"
  set :scm, "git"
  set :branch, "master"
  set :deploy_via, :remote_cache

The first line ensures that password prompts are passed through. The next three lines tell Capistrano where the repository is, that it is a Git repository, and which branch to use. The last line tells Capistrano to keep a cache of the repository on the web server, so it only has to transfer new code from the repository when deploying a new version.

If you are deploying from your own repository, you’ll need to change the repository URL, and if it isn’t a public repository, you’ll want to set up an SSH key pair, with the private key on your server and the public key added to your Github account; see the help docs on Github for details.

Then we need to tell Capistrano about our servers:

  set :application, "learningrails"
  set :deploy_to, "/var/www/apps/#{application}"
  set :user, "deploy"
  set :admin_runner, "deploy"
  
  role :app, "sampleapp.learningrails.com"
  role :web, "sampleapp.learningrails.com"
  role :db,  "sampleapp.learningrails.com", :primary => true

The application name is used to name the folder into which the application code is deployed. The second line allows us to specify where this folder is located on the system, with the application name substituted in as a variable. Then we tell Capistrano what user name to use when logging into the server. (It is customary to use a dedicated user name just for deployments.)

By default, Capistrano uses sudo to execute commands on the server. This won’t work on most shared servers, where you don’t have sudo access, so you might need to add the option set :use_sudo, false. If you do so, however, Capistrano might not be able to create the directories for your application. We’ve left sudo enabled, and used the admin_runner variable to set the user name under which Capistrano will use the sudo command when creating directories. This is what worked best for us in our server configuration, but your mileage may vary; if in doubt, check with your hosting company. (Versions of Capistrano prior to 2.4 behave somewhat differently.)

The three “role” lines tell Capistrano about our servers. We’re using a very simple configuration, in which the web (HTTP), application (Rails), and database (MySQL) servers are all on the same machine, but they don’t have to be. You can also have a cluster of servers, with more than one server performing each role, and Capistrano will take care of, for example, checking out a copy of the code to each application server, and modifying the setup on each web server to display a maintenance page.

Finally, you need to tell Capistrano how to start and restart the application server. There are standard Capistrano tasks for this that are designed for Mongrel and similar application servers; for Passenger, things are simpler, but we need to tell Capistrano just what to do, with these lines of code:

  namespace :deploy do
    desc "Restart Application"
    task :restart, :roles => :app do
      run "touch #{current_path}/tmp/restart.txt"
    end
    desc "Start Application -- not needed for Passenger"
    task :start, :roles => :app do
      # nothing -- need to override default cap start task when using Passenger
    end
  end

The namespace block tells Capistrano what name prefix to use for these tasks. Namespaces help avoid unintentional name conflicts in complex setups.

To restart Passenger, you only need to update the modification date of the restart.txt file, which is done with the touch command. Note that you can simply write run and then the command, and Capistrano knows to open an SSH terminal to the app server (as specified on the task line) and issue this command. current_path is a variable that provides the path to the current version of the application.

With Passenger, you don’t need to do anything to start the app server, but we need to define an empty start task to override the one that is part of the default Capistrano tasks.

These tasks give you a hint of what’s possible with Capistrano — you can use it to automate nearly everything you’d want to do on your server. Keep in mind, though, that if you use the :use_sudo, false setting, you can’t do things that require a privileged user.

We’re done changing code now, so if you’re working in your own repository, commit your changes now. We’ve already made all these changes in the public Github repository.

Install Passenger

Now we’re ready to set up the server. There’s a vast array of options here, depending on whether you’re on a shared or dedicated server, what operating system is installed, and so forth. To avoid a lot of system-specific discussion and keep this lesson to a reasonable length, we’re going to assume you have a Linux server with Apache 2.2, MySQL 2, and Git installed. If not, see the links at the start of this article for pointers to some resources, or check with your hosting company.

To install Passenger, simply enter in an SSH terminal window connected to your server:

  sudo gem install passenger

And then:

  passenger-install-apache2-module

The Passenger installer will check for any required dependencies and give you specific instructions if there’s anything else you need to set up first.

Configure Apache

Now we need to configure Apache. Your system should have a base Apache configuration already, often in /etc/httpd/conf/httpd.conf, though this varies depending on the Linux variant and system configuration.

We need to add a few lines to the Apache configuration file to tell Apache to use the Passenger module:

LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3/ext/apache2/mod_passenger.so
PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-2.0.3
PassengerRuby /usr/bin/ruby

The Passenger installer will provide these lines for you; the paths and other details may be different for your system configuration.

Then we need a virtual host configuration for our site. We put this is a separate file, which is included by reference at the end of the main Apache config file. Here’s what we used:

  <VirtualHost *:80>
     ServerName sampleapp.learningrails.com
     DocumentRoot /var/www/apps/learningrails/current/public 
     <Directory "/var/www/apps/learningrails/current/public">
          Options FollowSymLinks
          AllowOverride None
          Order allow,deny
          Allow from all
    </Directory> 
  </VirtualHost>

The first line identifies the root URL for your site. We’ve used the URL for our demonstration server, but you’ll want to use your own server URL here. You’ll need to set up a DNS record to point this name to the IP address of your server.

The second line must point to the public directory of your Rails application. We told Capistrano to put the application in /var/www/apps/learningrails, and as we’ll see shortly, current is a symlink that points to the currently active version.

The Directory directives ensure that Apache is allowed to access the public directory. This may or may not be necessary, depending on what the rest of your Apache config file looks like.

Now restart Apache, and make sure there are no errors reported. On our server, this can be done with:

  sudo /sbin/service httpd restart

Once again, the command may be different for your server configuration.

Set up MySQL

We have just one more thing we need to do on the server: create the empty database. You can do this with a MySQL GUI application, if it can connect to your server (typically using an SSH tunnel), or you can do it from the command line in a SSH terminal session. To use the latter approach, first create the database with the following command at the shell prompt:

  mysqladmin -u root -p create learningrails_production

This command assumes that you have a user configured for MySQL named root, and you’ll be prompted for root’s database password after you enter this command.

Then, start the mysql monitor program by entering

  mysql -u root -p

and create the deploy user by entering the following command at the mysql prompt:

mysql> grant all privileges on *.* to 'deploy'@'localhost' identified by 'secret';

(Note that you don’t enter mysql>; that’s the prompt.) The user name (deploy) and password (secret) must correspond to those in your database.yml file’s production section.

Your first deploy

Now, on to Capistrano tasks. First, to get an overview of all the Capistrano commands available, enter (in a terminal on your development system):

  cap -T

Remember, Capistrano always runs on your development system, even though it is issuing commands to your server.

Now there’s some one-time-only setup:

  cap deploy:setup

Capistrano will connect to your server, and will prompt you for the server password if don’t have an SSH key set up. Capistrano will then create some directories to hold your application code.

To make sure everything is ok, enter:

  cap deploy:check

Hopefully, this tells you that everything looks good; if not, read the error messages carefully and correct any problems. If there are permissions issues, Capistrano may not be able to create the directories it needs. You may need to log into the server and create the /var/www directory manually. For our server, the owner of this directory is set to deploy, and the group to apache.

Now, just enter:

  cap deploy:cold

And you should see lots of lines fly by, as Capistrano tells the server to check out the code from Github and sets up some symlinks. At the end, all the migrations should be run.

If all has gone well, you should now be able to browse to your server and see the application running. (You can visit http://sampleapp.learningrails.com to see the copy that we deployed.)

Troubleshooting

There’s a lot that can go wrong in this process, and unless everything works, your app won’t. So watch the Capistrano scripts carefully while they’re running. If there were no errors there, but it hangs on checking out the code, then there’s a problem with the authentication with Github (if you’re using a private repository) or the Git URL.

If you get no response when you try to access the application, make sure your DNS is set up correctly and has propagated to your computer, and that Apache is running. The Apache error log file may be helpful (the location varies depending on the system configuration; on our system, it is /var/log/httpd/error_log). If you get a Rails error page (error 500), then check the Rails log on the server for clues (/var/www/apps/learningrails/shared/log/production.log).

Because of the wide range of issues that can come up, it’s unlikely that we’ll be able to answer all your questions here. Here’s some resources to try:

And, of course, there’s always the tutorials, FAQs, and support staff at your hosting company.

Deploy, and deploy again

Once this is all set up and working, deploying new versions is gloriously simple. First, be sure to commit your changes and push them to Github. Then, just:

   cap deploy

Or, if you’ve added any migrations:

    cap deploy:migrations

If you want to put up a maintenance page during deployments, first issue this command:

   cap deploy:web:disable

And then do the deployment. When it’s done, just:

   cap deploy:web:enable

Something wrong? Just roll back

Another great thing about Capistrano is that it maintains multiple versions of your application on your web server. In the directory /var/web/apps/learningrails, you’ll find three directories: shared, releases, and current.

  • shared is for information that doesn’t change with each deployment. Log files, for example are stored in shared/log. If you have assets that are uploaded to the server and don’t go in the database or the repository, then they should go here, and you’ll want to add a Capistrano task (which can be automatically executed after each deployment) to symlink to the shared location from the current public directory.
  • releases has a directory for each deployed version. By default, Capistrano keeps the most recent four versions.
  • current isn’t a real directory; it’s a symlink to the current release directory. So you can always refer to the current version as /var/www/apps/learningrails/current, and you’ll really be accessing one of the directories inside releases.

So what’s the use of all these releases directories? If you deploy a version and then find that your application has a serious problem, just roll back:

   cap deploy:rollback

This task takes only a moment to execute, because all it has to do change the current symlink to point to the previous releases directory.

And there’s more…

We’ve just scratched the surface in this lesson, as deployment is a very complex topic. There’s much more you can do with Capistrano, and more that you should know to create a secure production deployment. Check out the links early in this lesson page to dive deeper.

Next lesson, we’ll look at how to evaluate the performance of your deployed application and find performance troublespots using New Relic’s RPM service.


Add Your Comments

(not published)

Reader Comments

20 comments

Successful after opening port 9418

From: Wayne Simacek, 10/05/09 05:50 AM

After much Googling, I finally found a suggestion that GitHub needed port 9418 opened in the firewall. Success! My deployment worked without error after that. The HostGator support were really supportive after I knew what to ask for. I'm now excited to proceed. Thanks, Wayne

Baby Steps, Git clone now works from server...still problems with Git Fetch Script

From: Wayne Simacek, 10/04/09 08:27 AM

I finally handed in my "real man" card and put in a ticket to HostGator. They found they needed to open outbound port 22 and now I can do a Git Clone from the server to my GitHub. However, my cap deploy:cold still gets fatal: unable to connect a socket (Connection refused). I'm thinking it's in my deploy.rb file? Could it be because I named my application differently than the long name in GitHub? I'm still using the following: set :scm, "git" set :repository, "git://github.com/WSimacek/learning-rails-sample-app.git" # ssh_options[:forwarding_agent] = true set :branch, "master" set :deploy_via, :remote_cache Any ideas? [I'm back to Googling for answers, but nothing much yet.] Thanks, Wayne

HostGator ssh: connect to host github.com: Connection refused

From: Wayne Simacek, 10/04/09 02:13 AM

Well, I've been working on my deploy lesson for about a week and pretty frustrated! I can't seem to connect to GitHub from my server any way. I've tried importing keys, creating keys, changing ports, Googled every link imaginable. I just can't believe there's no way. Wayne

HostGator went ahead and installed git client on the server!

From: Wayne Simacek, 09/30/09 08:53 AM

Great news!!! HostGator went ahead and installed git client on the server! [Unsupported...but a great start]. I re-ran my $cap deploy:cold, but now get a fatal: unable to connect a socket (Connection refused) ...probably from GitHub. I wonder if I have to put my server's public key on GitHub? Thanks in advance for any advice or suggestions. -Wayne

HostGator does not offer Git under shared or reseller servers

From: Wayne Simacek, 09/30/09 07:10 AM

After my Passenger and SSH rejection by GoDaddy, I opened up another shared hosting account on HostGator (I saw they had Passenger-2.2.5 installed so I thought I was home free.) Everything worked fine until it got to $cap deploy:cold and then it didn't recognize any of the git fetch commands. I thought maybe I could just put a path to git in my deploy.rb file, but they came back and said they don't support git on any of their shared plans. Since I've been "between gigs" since April, I wonder if you have any other "low cost" ideas on how I can deploy and still use version control? Thanks, Wayne

capistrano-2.5.9 task :stop expected a block

From: Wayne Simacek, 09/28/09 05:45 AM

I'm not trying to do a cap deploy:setup on my new host gator site. I'm having problems with task :stop {} in my deploy.rb file. >>I get a syntax error, unexpected '{'. >>When I blow out the {} I get an artument error that 'task':expected a block (ArgumentError). Do you think it needs an empty , roles => :app do? Any help will be appreciated. I don't think installing an earlier version of capistrano is an option on this server. -Wayne

godaddy problems

From: Michael Slater, 09/26/09 04:16 PM

We've heard lots of complaints about godaddy hosting for Rails apps, and lot of good things about Slicehost. Or, if your budget is a little bigger, Rails Machine.

GoDaddy will not allow Passenger Gem

From: Wayne Simacek, 09/26/09 02:34 PM

Well my deployment came to a screeching halt. I SSH'd into my shared Linux GoDaddy environment, and found out I don't have sudo permission and they won't install or support the Passenger gem. Looks like I'm off to find another hosting environemnt. Sucks to be me right now. --Wayne

rake aborted

From: ralf, 02/06/09 02:02 AM

Hi, I have solved the problem. The reason was that git did not include the plugins since they was a .git directory in each of the plugin directories. After deleting those .git directories git included the plugins and everything worked. Thank you! Ralf

rake aborted

From: ralf, 02/05/09 03:10 PM

Hi, Thanks for the great sceencast. everything went smothely so far. Now just a few millimeters before running on the deployed server the following error occured: the cap deploy:cold aborted with : rake RAILS_ENV=production db:migrate (in /www/ra1/releases/20090205222651) rake aborted! undefined method `acts_as_textiled' for # As I understand, the plugings come with the deployment. Here are the gems of the remote server: actionmailer (2.2.2) actionpack (2.2.2) activerecord (2.2.2) activeresource (2.2.2) activesupport (2.2.2) fastthread (1.0.1) fcgi (0.8.7) mysql (2.7) passenger (2.0.6) rack (0.9.1) rails (2.2.2) rake (0.8.3) RedCloth (4.1.1) Any feedback is highly appreciated! Regards, Ralf

deploy:cold problem

From: Chris Schmitt, 11/04/08 07:12 PM

Thanks Michael, you were bang on. I had changed the namespace from deploy to my user name and that prevented the deploy task from executing. Works like a charm now. Great series - thank you!

deploy:cold problem

From: Michael Slater, 11/04/08 10:21 AM

Chris, Are you sure you included this bit of code in the deploy task definition? task :start, :roles => :app do # nothing -- need to override default cap start task when using Passenger end They symptom you're seeing is from capistrano trying to use it's default start task.

cap deploy:cold fails at the very end

From: Chris Schmitt, 11/04/08 08:58 AM

Hi, hope you can help. Everything works fine until the very end when I receive the following error message: ** [out :: cavas.ca] nohup: cannot run command `script/spin': No such file or directory Any ideas? It seems that capistrano thinks it's trying to start a mongrel server instead of Phusion.

Simply Great

From: Bharat Ruparel, 10/24/08 07:43 AM

Michael/Christopher, I just dug into your online screencasts and this one is simply great. I followed the instructions and have Passenger up and running on my home network. Are you planning on continuing your online educational efforts? Free or paid, doesn't matter. If you can do a screencast on enhancing the look and feel of a Rails application that would be super. Knowing you gents, it is bound to have sufficient depth. Excellent work and keep it up.

topics

From: Alexei, 10/08/08 11:28 PM

Thank you, guys. It's a tremendous work you are doing! It takes so much time and efforts. What do you think, is there a way for us, listeners of your *casts to propose a topic(s) for some more episodes? Maybe I am asking too much, but you are so helpful, I can't help myself :)

MORE! MORE! MORE!

From: Amadeu Tolentino, 09/26/08 03:07 PM

Thanks for helping me gain a better understanding of RoR. Hopefully, there will be plenty more to come. Great Work!

Future screencasts

From: Michael Slater, 09/16/08 08:01 AM

We have at least one more coming. Maybe more...

Is this the last one?

From: Umut Uygar, 09/16/08 05:25 AM

Thanks a million for this great series. I learned so much. Is this this last screencast in the series?

Good stuff

From: Per Velschow, 09/11/08 03:08 AM

Thanks for another great screencast. As always very informative and entertaining. Looking forward to the next one. Keep up the good work. :)

Thank you

From: Ayres Narita, 09/10/08 07:21 AM

Thank you guys, very useful information.