Skip to content

Instantly share code, notes, and snippets.

@vishaltelangre
Forked from jpantuso/README.md
Created March 5, 2013 12:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vishaltelangre/5090045 to your computer and use it in GitHub Desktop.
Save vishaltelangre/5090045 to your computer and use it in GitHub Desktop.

Deploying Rails to Linode

Installing Ruby Enterprise Edition, Apache, MySQL, and Passenger for deploying Rails 3.0 applications.

Get a Linode, and set it up with Ubuntu 10.04 LTS so that you have till April 2013 to get updates. Once the Linode is formatted, boot it and continue on.

Set up an 'A' record in your DNS, pointing to the IP of your Linode. I'm using demo.napcs.com here.

Initial setup

Log in with ssh root@your_ip

Add a user

adduser deploy

Add the user to the SUDOERS list with visudo

# User privilege specification
root    ALL=(ALL) ALL
deploy   ALL=(ALL) ALL

Change the default SSH port and disallow root acces. vim /etc/ssh/sshd_config

# What ports, IPs and protocols we listen for
Port 16888

...
PermitRootLogin no

Logout and reboot your linode via the console. Don't attempt to restart SSHD while connected. :)

Test the new settings - Log back in with ssh deploy@your_ip -p 16888 and log out again

Create your authorized keys file

mkdir ~/.ssh
touch ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

Logout and upload your public key

cat ~/.ssh/id_rsa.pub | ssh deploy@your_ip -p 16888 'cat >> .ssh/authorized_keys'

That looks a little complex, but we're just printing out the SSH key, piping the output to SSH and directing it into the authorized_keys on the remote server. You could do this many ways, but this is something you could turn into a script and use this to add multiple keys.

Log back in and you're set with keys.

Installing Git and Apache (we need git for ZSH)

sudo apt-get install apache2 git-core

Now disable the default site.

sudo a2dissite default
sudo /etc/init.d/apache2 restart

Install ZSH

sudo wget --no-check-certificate https://github.com/j2fly/oh-my-zsh/raw/master/tools/install.sh -O - | sh
  1. Edit the ~/.oh-my-zsh/lib/rvm.zsh so that it points to /usr/local/rvm/bin/rvm-prompt

  2. edit the ~/.oh-my-zsh/themes/j2fly.zsh-theme so that it points to /usr/local/rvm/bin/rvm-prompt as well

  3. Edit your ~/.zshrc so that it has the RVM stuff in it.

  4. You may also need to edit/move the export PATH= line to be above the zsh load line

Modify .gemrc file in your root directory

--- 
gem: --no-rdoc --no-ri
:benchmark: false
:backtrace: false
:update_sources: true
:verbose: true
:bulk_threshold: 1000
:sources: 
- http://rubygems.org/

Installing Ruby

We'll use Ruby Enterprise Edition through RVM

  1. Install RVM

    bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

  2. Install some required libraries to allow for RVM to build REE

    sudo apt-get install build-essential patch zlib1g-dev libssl-dev libreadline5-dev

  3. Install Ruby Enterprise Edition

    rvmsudo rvm install ree

  4. Use the global gemset and install bundler into it

    rvm gemset use global gem install bundler

  5. set default ruby

    rvmsudo rvm --default use ree

Installing MySQL for Rails

We install MySQL with the package manager:

sudo apt-get install mysql-server libmysqlclient-dev
gem install mysql

Now log into mysql with mysql -u root -p and create a user

create user 'rails'@'localhost' identified by 'rails';
create databse projects_production;
grant all on projects_production.* to 'rails'@'localhost';
SET PASSWORD FOR 'rails'@'localhost' = PASSWORD('newpass');

These permissions are not the greatest. They could be hardened. But you have to make sure you don't tighten them too much to the point where you can't run migrations. Read a good MySQL book.

Configuring Apache and Passenger

rvm use ree
rvm gemset use global
rvmsudo gem install passenger
sudo apt-get install libcurl4-openssl-dev apache2-prefork-dev  libapr1-dev  libaprutil1-dev

rvmsudo /usr/local/rvm/gems/ree-1.8.7-2011.03@global/bin/passenger-install-apache2-module

Follow the instructions.

CD to /etc/apache2/conf.d and add a file called "passenger.conf" which has the following contents:

LoadModule passenger_module /usr/local/rvm/gems/ree-1.8.7-2011.03@global/gems/passenger-3.0.7/ext/apache2/mod_passenger.so
PassengerRoot /usr/local/rvm/gems/ree-1.8.7-2011.03@global/gems/passenger-3.0.7
PassengerRuby /usr/local/rvm/wrappers/ree-1.8.7-2011.03@global/ruby

Create a new vhost

sudo vim /etc/apache2/sites-available/demo.napcs.com

Put this code in the file:

<VirtualHost *:80> ServerName demo.napcs.com DocumentRoot /var/www/demo.napcs.com/current/public
<Directory /var/www/demo.napcs.com/current/public> AllowOverride all
Options -MultiViews

Then enable the site

sudo a2ensite demo.napcs.com
sudo /etc/init.d/apache2 restart

And give the deploy user permissions to the /var/www root folder:

sudo chown -R deploy:deploy /var/www 

Again, not the best approach, but an acceptable one.

We're done with server setup!

Preparing Your Rails app

Create the Rails 3 application. We'll do a simple one with a single table.

rails new demo
cd demo
rails g scaffold projects name:string description:text
rake db:migrate
rm public/index.html

Add a root route to config/routes.rb

root :to => "projects#index"

Add the mysql2 gem to your Gemfile and get the sqlite3 one only in dev mode. Specify the version numbers for the gems or you'll get burned!'

group :development do                                                                                                                                                                                
  gem 'sqlite3', '1.3.3'
end

group :production do
  gem 'mysql2', '0.2.7'
end

Then

bundle

Let's deploy!

Capistrano setup

Generate the capistrano config file:

capify .

Modify the config/deploy.rb file with our settings for our server.

require 'bundler/capistrano'

set :application, "demo.napcs.com"

# Deploy from your local Git repo by cloning and uploading a tarball
set :scm, :git
set :repository,  "."
set :deploy_via, :copy

set :user, :deploy
set :deploy_to, "/var/www/#{application}"
set :use_sudo, false

role :web, "demo.napcs.com"                          # Your HTTP server, Apache/etc
role :app, "demo.napcs.com"                          # This may be the same as your `Web` server
role :db,  "demo.napcs.com", :primary => true # This is where Rails migrations will run

namespace :deploy do
  task :start do ; end
  task :stop do ; end
  task :restart, :roles => :app, :except => { :no_release => true } do
   run "#{try_sudo} touch #{File.join(current_path,'tmp','restart.txt')}"
  end
end

Pay attention to this part:

set :scm, :git
set :repository,  "."
set :deploy_via, :copy

This lets you from your local Git repo by cloning and uploading a tarball. This means you can push code from your computer to your server, rather than having to pull code from your remote Git repository to your server. Or if Github is down.

Now specify your production database info in database.yml:

production:
  adapter: mysql2
  host: localhost
  database: projects_production
  username: rails
  password: rails

Here, we're putting our production db password in our database.yml file. You probably want to keep this file out of your repository, and instead write a custom Capistrano task to build the database.yml file during deployment. That's something you can Google on your own.

Now make a Git repo

git init
git add .
git commit -a -m "Initial import"

Create the application directory on the server:

cd /var/www/
sudo mkdir domain.com
sudo chown deploy:www-data domain.com
sudo chmod 775 domain.com

Finally, set up the app to the server:

cap deploy:setup

Deploy the app

cap deploy:cold

And we're done! At this point, make changes to your code, commit them, redeploy with

cap deploy

Bonus: Stream your logfiles!

Add this to deploy.rb

desc "tail production log files" 
task :tail_logs, :roles => :app do
  run "tail -f #{shared_path}/log/production.log" do |channel, stream, data|
    puts  # for an extra line break before the host name
    puts "#{channel[:host]}: #{data}" 
    break if stream == :err    
  end
end

Then run it with

cap tail_logs

Capistrano isn't just for deploying Rails apps. You can use it to automate anything you do with SSH.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment