In this post I will show you how to install Capistrano 3 for deployments to your dedicated/VPS server. Things are little bit different (and easier) when you deploy to the cloud (heroku), but clouds are expensive (up to 4 times in my case). So let’s play and have fun with setting all the things manually and save some money.
This post assumes that you have followed my previous articles on setting up Ruby/Rails production environment: How To Install RVM (Ruby Version Manager) and How to install Passenger for Nginx + few tricks
Here is the very simple diagram we gonna follow:
Dev -- Prod -- BitBucket
Dev is our development machine. It can look little bit different if you run it on Windows or on Mac (see my article Easy Rails with Vagrant + VirtualBox on Windows about how to run Ruby and Rails on Windows with Vagrant):
+-------------------+ +-------------------+
| Windows | | |
| +--------------+ | | |
| | linux | | | Mac |
| | running | | or | Dev machine |
| | in vagrant | | | |
| +--------------+ | | |
+-------------------+ +-------------------+
What you need to understand here is that you have to set the link between Dev and Prod. Prod is our production server. In case of running Windows with Linux and Vagrant, you have to set the link between the the Linux inside of Windows and production server. If you’re on Windows, we’ll run deployment process from this Linux box, keep this in mind. On Mac OS it is little bit easier.
Okay, but how to set the link up and what does it mean – “set the link between Dev and Prod”? It is easy, you just need to be able to log in from Dev to Prod by ssh without any passwords. Here is the command you can use to check if this link is already configured:
dev$ ssh 'deploy@forum.rubyschool.us'
Not that I’ll use forum.rubyschool.us for hostname here and fake IP of ‘144.76.1.222’, it’s the same. We’ll deploy the website ‘devhq.io’, which is located on the same host. And I’ll use “dev$” and “prod$” prefixes to show where command should be executed.
The command above should reply back with the error message, because the link is not set yet. So let’s do that. The first step is to create deploy user. Let’s create user for deployment and add it to sudo group.
prod$ sudo adduser deploy
prod$ sudo adduser deploy sudo
Then let’s edit sudoers file:
prod$ sudo nano /etc/sudoers
add change this line:
%sudo ALL=(ALL:ALL) ALL
to this:
%sudo ALL=(ALL) NOPASSWD:ALL
So all sudo users won’t need to type password for sudo commands. Passwordless sudo is required in some cases for capistrano. See capistrano docs for more. Here also you might want to take a look at sudoers file to see if deploy user is not mentioned there already. If it is, make sure he is passwordless.
You can check if sudoers file was updated correctly by executing:
Now we have passwordless sudoed user with login ‘deploy’. It’s half the battle only. Passwordless sudoes means that used won’t be asked for password when s/he types “sudo something”. But when you try to login from Dev to Prod with ssh:
dev$ ssh deploy@forum.rubyschool.us
…you’ll be asked for the password. We don’t want that and we need passwordless login. Let’s copy ssh certificate from Dev to Prod for ‘deploy’ user. Check if you already have ssh certificate generated on your Dev for logged in user:
dev$ ls -la $HOME/.ssh/
You should see “id_rsa.pub” file listed. If it’s not there, it’s time to generate new one. Don’t type password when prompted, just leave it blank:
dev$ ssh-keygen
Great! Now you have your certificates generated on your Dev machine. Let’s copy public certificate to Prod so you won’t need to type password when logging in. Copying is easier with ssh-copy-id command:
dev$ ssh-copy-id deploy@forum.rubyschool.us
Now you can try to login from Dev to Prod with ssh command to check if it works. You should be able to that without a password:
dev$ ssh deploy@forum.rubyschool.us
Now Capistrano can use this feature for deployment purposes. Cool! What’s next?
Let’s think about another link on our diagram: Prod to Bitbucket (GitHub). Production server should have access to your code repository. Let’s check if you have this access already. Execute this command on your server, but under ‘deploy’ user(su deploy command). It’s important, because you may have access via ssh certificate from one user, and don’t have an access from another one.
deploy@prod$ git ls-remote --heads git@bitbucket.org:ro31337/devhq.git
If you don’t have git installed on your server, you can install it with:
prod$ sudo apt-get install git
But wait a second, where does this git link (git@bitbucket.org:ro31337/devhq.git) come from? Well, you can find it on your Bitbucket or Github settings page for your source code repository, or you can just simply check .git folder (it’s can be hidden) in your source code repository. In config file you’ll find url property to repository. The output for ‘git ls-remote… ‘ command is something like:
4ce15674cb4e0a0c12c726eb5e586e7655f880fb refs/heads/master
To be honest, you should get an error at this point, because the link between Prod and Bitbucket was not set. We’ve created ‘deploy’ user on Prod, but the certificate for newly created user is not present at Prod. Let’s check if you have one (run this command on production server, we have already installed certificates on development machine, but it is not Dev machine!):
prod$ ls -la $HOME/.ssh/
See if “id_rsa.pub” is there. If it’s not, run ssh-keygen command on Prod. Now we should have “id_rsa.pub” file created and it’s time to upload contents on this file to your Bitbucket (or Github). I usually do it by output the file contents to the console and copying it from there:
prod$ cat $HOME/.ssh/id_rsa.pub
Copy the output to Bitbuket/GitHub SSH global settings and click save. Test if this command works now:
deploy@prod$ git ls-remote --heads git@bitbucket.org:ro31337/devhq.git
At this point we should have all the links up on the way from Dev to Prod, and from Prod to BitBucket. And now the funny starts: setting up Capistrano, woohoo!
I’ll cover 3.4+ version here, it probably work with 3.x Capistrano branch. Keep in mind that there is a big difference between version 2 and 3. Let’s install Capistrano gem. Run on your Dev machine:
dev$ gem install capistrano
See the hint at end of this article if you have permissions issues here.
Well, gem is installed. Now let’s update our Gemfile and then execute Capistrano installation procedure. Add to the end of your Gemfile:
Not that I’ve included ‘capistrano-passenger’ gem, because my server runs with Passenger on Nginx. If you use Unicorn server, things in this tutorial won’t work for you. Run bundle install and execute Capistrano installation procedure:
dev$ bundle install
dev$ bundle exec cap install
Output for the last command:
Great! We’ve been “capified” and we have a set of new files in the project. In very basic scenario you have to modify two of these files: production.rb and deploy.rb. The first one is very easy, I’ve just added one line in my production.rb:
server '144.76.1.222', user: 'deploy', roles: %w{web app}
Where ‘144.76.1.222’ is address of your server. Things are little bit tricky for deploy.rb, so I’ll just mention my uncommented ‘set’ statements here:
deploy_to is directory where our website will be hosted. Create it now if you don’t have one:
prod$ mkdir -p /var/www/yourprojectname/
Before we execute the Capistrano deploy command (remember it now: bundle exec cap production deploy), I want you to know about Capistrano deployment features. There will be two top-level directories on your production server in /var/www/yourprojectname/, releases and shared:
releases
|- current
|- ...
shared
Releases directory will contain ‘current’ release and other releases directories (they will look like a datestamp: 20150507185726). Shared will keep the shared data between your releases in time, so this data won’t be overwritten by new deployments. It could be your config files with sensitive information: you probably don’t want to keep sensitive information in your source code repository. It could be any other data, like user uploads. So uploading sensitive files once and keeping them there sounds like a good idea. And we can keep database.yml and secrets.yml files in this directory.
The implementation behind this idea is very easy. On deployment, config/database.yml and config/secrets.yml will be symlinked to correspondent files in shared directory. Nothing fancy!
Now let’s run Capistrano and let it fail:
dev$ bundle exec cap production deploy
The output is
ERROR linked file /var/www/devhq.io/shared/config/database.yml does not exist on 144.76.1.222
It has failed because we don’t have any files in shared folder. Capistrano should create a symlink, but it can’t. Let’s upload these files. We could create additional Capistrano task for that, but looks like overhead for the simple demo. So instead of creating a task, let’s upload them with the scp command (assuming you’re located at project directory):
dev$ scp config/database.yml 'deploy@144.76.1.222:/var/www/devhq.io/shared/config/'
dev$ scp config/secrets.yml 'deploy@144.76.1.222:/var/www/devhq.io/shared/config/'
You should be okay now and your website should be deployed successfully. If not, keep on reading, I’ll show you some hints. You can get the following error on fresh Ubuntu installation:
ExecJS::RuntimeUnavailable: Could not find a JavaScript runtime
But it’s easy to resolve with:
prod$ sudo apt-get install nodejs
Another error you may have:
You are not authorized to query the status for this Phusion Passenger instance
Check if you have the following two lines in deploy.rb:
set :passenger_restart_command, "touch #{release_path}/tmp/restart.txt"
set :passenger_restart_options, -> {}
It’s an old way of restarting passenger without sudo permissions required.
If you have
sudo: no tty present and no askpass program specified
Make sure you have specified in your deploy.rb:
set :pty, true
If you have other errors, login under root and deploy user on Prod and make sure these commands work:
rvm current
rvm list
rvm use ruby-2.2.0 # your ruby version,
If you have something like ‘not found’ error, make sure you have installed and executed everything under these both users (deploy and root):
sudo apt-get install rvm
Try to run deployment process again:
dev$ bundle exec cap production deploy
It should work, but I also had fancy output for ‘rvm use ruby-2.2.0‘ command under deploy and root user:
RVM is not a function, selecting rubies with 'rvm use ...' will not work.
One-time solution is to run:
sudo apt-get install rvm
source ~/.rvm/scripts/rvm
type rvm | head -n 1
rvm requirements
rvm use ruby-2.2.0
But when you log out, your changes won’t be saved. To permanently resolve this, you should copy this line from .bash_history to .bashrc:
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
Finally, the last Capistrano command will finish with something like:
[0d37c034] Finished in 0.584 seconds with exit status 0 (successful).
Congratulations! Your app has been deployed.
Keep in mind that it was very simple deployment scenario, something just to start from. Hopefully, later I’ll cover more complicated scenarios. Good luck!
Useful commands:
See also:
http://www.talkingquickly.co.uk/2014/01/deploying-rails-apps-to-a-vps-with-capistrano-v3/
http://corlewsolutions.com/articles/article-10-how-to-deploy-rails-applications-using-capistrano-3-1-and-windows-7