Deploying your rails app on slicehost with git (github), nginx virtual host, and thin

UPDATE July 13, 2008: see the very bottom for deploy.rb file that includes cron job and namespaces some of the deploy tasks.


This article assumes you already have git nginx, and thin setup properly on your slicehost. To get those going I recommend reading their articles at articles.slicehost.com, and browse their forum for git installation.

0. Make sure you server is setup to talk to github. Github has a guide

1. Capify your rails app from inside the root of your app in terminal

capify .

2. Edit the deploy.rb file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# here is a good standard example
set :application, "undertakerapp"
# Change this appropriately depending on what site you are testing
set :domain, "domain.com"  # your domain.com for this app
set :user, "username" # your username on slicehost
 
#from http://github.com/guides/deploying-with-capistrano
default_run_options[:pty] = true
set :repository,  "git@github.com:scottmotte/motte_cms.git" # your repository. this could be anywhere, but i recommend github
set :scm, "git"
set :scm_passphrase, "password" #github password
set :user, "username" #github username
set :branch, "master" #this is the branch you want. most likely master
set :deploy_via, :remote_cache
 
#from hostingrails.com/forums/wiki_thread/46
set :use_sudo, false
set :port, 8000  # your port on slicehost. Standard port for ssh is 22, but if you followed the slicehost articles, you probably changed this to something different
set :deploy_to, "/home/#{user}/public_html/#{application}"          # Where on the server your app will be deployed
set :chmod755, "app config db lib public vendor script script/* public/disp*"  	# Some files that will need proper permissions
 
# If you aren't deploying to /u/apps/#{application} on the target
# servers (which is the default), you can specify the actual location
# via the :deploy_to variable:
# set :deploy_to, "/var/www/#{application}"
 
# If you aren't using Subversion to manage your source code, specify
# your SCM below:
# set :scm, :subversion
 
role :app, domain
role :web, domain
role :db,  domain, :primary => true
 
desc "Reload Nginx"
task :reload_nginx do
  sudo "/etc/init.d/nginx reload"
end
 
desc "Restart Thin"
task :restart_thin do
  sudo "/etc/init.d/thin restart"
end
 
after "deploy", "deploy:cleanup"
after "deploy:cleanup", "reload_nginx"
after "reload_nginx", "restart_thin"

3. Fix up your database.yml: make sure the production database settings are specified as follows

1
2
3
4
5
6
7
production:
  adapter: mysql
  encoding: utf8
  database: yourapp_production
  username: mysql_user # the mysql username you are going to use. this could be root, but it probably isn't the best idea to use root
  password: password # the mysql password you are going to use.
  socket: /var/run/mysqld/mysqld.sock

4. Setup your database on slicehost: This is easy(er) if you have phpmyadmin. To do it in the terminal do the following.
- ssh into your slicehost
- type mysql -u mysql_user -p (replace mysql_user with your mysql user. this could be root)
- a successful entering should show you the mysql> prompt (mysql>)
- type show databases;
- if it has not been created do

CREATE DATABASE yourapp_production;

5. Exit your server and go back to your local command prompt and cd inside your railsapp. Type the following:

1
2
3
4
5
6
7
8
cap deploy:setup
# wait for that to complete then...
 
cap deploy:cold
# wait for that to complete then you might need to do...
 
cap deploy:migrations
# any other changes you commit to github and then want to deploy, you can now just run 'cap deploy' (unless you have new migration files, in which case run 'cap deploy:migrations')

6. Thin configuration:
Follow some of these steps to get thin setup, and your app symlinked

ssh into your slicehost

cd ~/public_html/railsapp/current folder

sudo thin config -C /etc/thin/railsapp.yml -c /home/username/public_html/railsapp/current/  --servers 3 -e production

IMPORTANT NOTE: If you are doing a second app on the same server through vhosts (like so) I’m pretty sure you need to change the default port in the the thin config file. 3000 will already be used by the original one you created.

Therefore, you might do something like this:

sudo thin config -C /etc/thin/railsapp.yml -c /home/username/public_html/railsapp/current/  --servers 3 --port 3001 -e production

check it exists:

cat /etc/thin/railsapp.yml

start it up:

sudo /etc/init.d/thin start

7. Nginx virtual host:
Add your site to the sites available

sudo nano /usr/local/nginx/sites-available/domain.com

Add server vhost and server info (Take your time doing this. There are a lot of places you could mistype things.)

upstream domain1 {
        server 127.0.0.1:3003;
        server 127.0.0.1:3004;
        server 127.0.0.1:3005;
    }
 
server {
            listen   80;
            server_name  www.domain.com;
            rewrite ^/(.*) http://domain.com permanent;
           }
 
server {
            listen   80;
            server_name domain.com;
 
            access_log /home/demo/public_html/railsapp/shared/log/access.log;
            error_log /home/demo/public_html/railsapp/shared/log/error.log;
 
            root   /home/demo/public_html/railsapp/current/public/;
            index  index.html;
 
            location / {
                          proxy_set_header  X-Real-IP  $remote_addr;
                          proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
                          proxy_set_header Host $http_host;
                          proxy_redirect false;
 
                          if (-f $request_filename/index.html) {
                                           rewrite (.*) $1/index.html break;
                          }
 
                          if (-f $request_filename.html) {
                                           rewrite (.*) $1.html break;
                          }
 
                          if (!-f $request_filename) {
                                           proxy_pass http://domain1;
                                           break;
                          }
            }
 
}

Now enable it.

sudo ln -s /usr/local/nginx/sites-available/domain.com /usr/local/nginx/sites-enabled/domain.com

8. Now do another of these on your local computer:

cap deploy

UPDATE:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
set :application, "mottemen"
# Change this appropriately depending on what site you are testing
set :domain, "mottemanagers.com"
set :user, "***username***"
 
#from http://github.com/guides/deploying-with-capistrano
default_run_options[:pty] = true
set :repository,  "git@github.com:scottmotte/mottemen.git"
set :scm, "git"
set :scm_passphrase, "***password***" #This is your custom users password
set :user, "***username***"
set :branch, "master"
set :deploy_via, :remote_cache
 
#from hostingrails.com/forums/wiki_thread/46
set :use_sudo, false
set :port, 1984
set :deploy_to, "/home/#{user}/public_html/#{application}"          # Where on the server your app will be deployed
set :chmod755, "app config db lib public vendor script script/* public/disp*"  	# Some files that will need proper permissions
 
role :app, domain, :cron => true
role :web, domain
role :db,  domain, :primary => true
 
namespace :cron do
 
  task :start, :roles => :app, :only => {:cron => true} do
    cron_tab = "#{shared_path}/cron.tab"
    run "mkdir -p #{shared_path}/log/cron"
 
    require 'erb'
    template = File.read("config/cron.erb")
    file = ERB.new(template).result(binding)
    put file, cron_tab, :mode => 0644
 
    # merge with the current crontab
    # fails with an empty crontab, which is acceptable
    run "crontab -l >> #{cron_tab}" rescue nil
 
    # install the new crontab
    run "crontab #{cron_tab}"
  end
end
 
namespace :cron do
 
  task :stop, :roles => :app, :only => {:cron => true} do
    cron_tmp = "#{shared_path}/cron.old"
    cron_tab = "#{shared_path}/cron.tab"
 
    begin
      # dump the current cron entries
      run "crontab -l > #{cron_tmp}"
 
      # remove any lines that contain the application name
      run "awk '{if ($0 !~ /#{application}/) print $0}' " +
        "#{cron_tmp} > #{cron_tab}"
 
      # replace the cron entries
      run "crontab #{cron_tab}"
    rescue
      # fails with an empty crontab, which is acceptable
    end
 
    # clean up
    run "rm -rf #{cron_tmp}"
  end
end
 
namespace :nginx do
  desc "Reload Nginx"
  task :reload do
    sudo "/etc/init.d/nginx reload"
  end
end
 
namespace :thin do
  desc "Restart Thin"
  task :restart do
    sudo "/etc/init.d/thin restart"
  end
end
 
after "deploy", "deploy:cleanup"
after "deploy:cleanup", "nginx:reload"
after "nginx:reload", "thin:restart"
after "thin:restart", "cron:stop"
after "cron:stop", "cron:start"

Comments

  1. Scott | August 23, 2008

    http://articles.slicehost.com/2008/7/10/mysql-creating-and-editing-users use that to create the users - instead of using the root user.

  2. Millisami | October 22, 2008

    @Scott,
    where’s that #{shared_path} var set to??

  3. scott | October 22, 2008

    @Millisami - shared path gets set automatically by capistrano to your apps shared folder. So in this case it was ~/apps/undertakerapp/shared

  4. Millisami | October 23, 2008

    @scott - Thanks for the reply.
    One more thing I forgot to ask about is the cron.
    Why “namespace :cron do” is in 2 namespace blocks and what its really doing?

  5. scott | October 23, 2008

    Yea, actually, you could put the stop and start tasks inside the same cron namespace. I don’t know why I put them separately. Didn’t really know what I was doing at the time I suppose. =)

    Also, just realized my blog strangled the > sign so everywhere you see > it should be the actually greater than sign >.