Capistrano is a Ruby-based server automaton and deployment tool that is primarily designed to deploy Ruby (on Rails) applications, but also works really well for deploying Node.js applications because of its sensible defaults.

I'm not going to explain exactly how Capistrano works, instead I'm just going to describe my Capistrano configuration for a simple Node.js application. To get started with Capistrano read through the documentation on their website and follow the instructions to set up Capistrano in your project.

Assuming you are using a single stage (e.g. 'production'), set up your stage file (probably config/deploy/production.rb) so that your server is responsible for all app roles:

# config/deploy/production.rb
server 'my.server.com', user: 'me', roles: %w{app db web}

Then set up your config/deploy.rb file to look something like this:

# config/deploy.rb
# config valid only for current version of Capistrano
lock '3.4.0'

set :application, 'my-application'
set :repo_url, '[email protected]:me/my-application.git'

set :deploy_to, "/home/me/Applications/#{fetch :application}"

# Link node_modules so it is shared between deploys.
# If you also have a log folder, or other folders that
# should be shared, add them here
set :linked_dirs, %w(
  node_modules
)

namespace :deploy do

  desc 'Copy upstart script'
  task :upstart do
    on roles(:app) do
      within release_path do
        sudo :cp, "etc/#{fetch :application}.upstart.conf", "/etc/init/#{fetch :application}.conf"
      end
    end
  end

  desc 'Restart application'
  task :restart do
    on roles(:app) do
      sudo :service, fetch(:application), 'restart'
    end
  end

  desc 'Install node modules'
  task :install_node_modules do
    on roles(:app) do
      within release_path do
        execute :npm, 'install', '-s'
      end
    end
  end


  after :updated, :install_node_modules
  after :updated, :upstart
  after :publishing, :restart

end
  • First, we specify that this configuration only works with the latest Capistrano version, which isn't strictly true, but is a safe default.
  • Next, we specify the name of the application, the location of the git repository and where we want to deploy to.
  • We instruct Capistrano to link node_modules between deploys. This means it will put node_modules in the shared folder in the root of the deployment location and symlink it in to each deployed version so that all the modules don't have to be reinstalled every time we deploy.

The deploy block describes the various tasks we want to run, and when to run them, you can also run them individually e.g. cap production deploy:restart.

  • Our deploy:upstart copies an upstart script from the deployed application directory into the correct place on the system using sudo for elevated permissions. This would require passwordless-sudo to be enabled. Capistrano has more information on how to do this.
  • deploy:restart is pretty self explanatory, using sudo again to restart the service with upstart.
  • deploy:install_node_modules does exactly what you would expect; runs npm on the server in the deployed directory.

Finally, we set up the deploy hooks to ensure these tasks run at the appropriate time.

Of course you would also need a web server (e.g. Nginx) to proxy to this Node.js application. You could keep the configuration for Nginx in the repository as well and copy it to the appropriate place much like the upstart script in the example above. You could even reload Nginx as part of the restart task.

This is the simplest configuration that can work, but it is easy to add additional functionality on top of this base configuration. Some examples of configurations I have used:

Locally building & uploading a set of static files

desc 'Locally build the static site'
task :build_static do
  run_locally do
    within 'static' do
      execute :grunt, 'build'
    end
  end
end
  
desc 'Upload the static site'
task :upload_static do
  on roles(:app) do
    upload! 'static/dist', release_path.join('static'), recursive: true
  end
end

before :check, :build_static
after :updated, :upload_static

Ensuring a file is present on the server

desc 'Ensure a file is present'
task :ensure_file do
  on roles(:app) do
    unless test '[ -f /srv/file ]'
      error " Missing file in /srv directory."
      exit 1
    end
  end
end

before :check, :ensure_file

Capistrano is a powerful, if slightly retro, tool. I hope this post is useful when you're next deploying a Node.js application :) let me know in the comments if you have a different preferred deployment method!