Skip to content

Instantly share code, notes, and snippets.

@joshteng
Last active July 28, 2016 06:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save joshteng/9901155 to your computer and use it in GitHub Desktop.
Save joshteng/9901155 to your computer and use it in GitHub Desktop.

Download and Install Vagrant http://www.vagrantup.com/ and VirtualBox https://www.virtualbox.org/ or VMware before beginning

#Set up Vagrant *this guide assumes the use of Mac OS X Mountain Lion on local machine and Ubuntu 12.04 LTS x64 on Vagrant box. It is compatible with many other distros but hasn't been tested.

##Step 1: Make and run box

vagrant init
vagrant box add <path to box directory or name according to https://vagrantcloud.com/>
vagrant up
vagrant ssh

##Step 2: Forward ports ####Vagrantfile

config.vm.network "forwarded_port", guest: 80, host: 8080
config.vm.network "forwarded_port", guest: 3000, host: 3000

#Set up environment You can choose to use configuration management/provisioning tools like Chef/Puppet for the following steps to set up your environment. But if you want to understand how it's done, I encourage you to do it manually.

##Step 3: Install needed system libraries and Postgresql

sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev libcurl4-openssl-dev python-software-properties libpq-dev postgresql postgresql-contrib

##Step 4: Install ruby

git clone git://github.com/sstephenson/rbenv.git .rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL

git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL

rbenv install 2.1.0
rbenv global 2.1.0
rbenv rehash

##Step 5: Install Bundler

echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler
rbenv rehash

##Step 6: Install Node.js for Rails asset pipeline

sudo add-apt-repository ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install nodejs

Step 7: Install Nginx on server

sudo apt-get install nginx

Step 8: Set up postgres 'vagrant' role

sudo su - postgres
createuser --pwprompt #create a role called vagrant.. just leave a blank password if you're using Vagrant locally for development and make it a superuser when prompted!
psql
ALTER USER Postgres WITH PASSWORD 'vagrant'; --run this in psql..I actually don't know why I had to do this.
\q --exit psql
exit

#Set up app

Step 9: Bundle install

cd /vagrant #this is where your rails app should be by default if you changed it, just go to the right path on your VM
bundle install
rbenv rehash

Step 10: Create and Migrate DB

bundle exec rake db:create
bundle exec rake db:migrate
  • if there's encoding issues: https://gist.github.com/joshteng/9895494
  • if you are facing cryptic errors (eg. wrong ELF class: ELFCLASS32) with bcrypt, pg etc+ gems just remove it from /vendor/bundle and re-run bundle install
  • if the above still doesn't solve your problem, remove all the gems from your rails app by doing rm -rf /vagrant/vendor/bundle and then run bundle install again. (unfortunately this takes ages depending on your internet connection and rubygems.org)
  • if you're getting error FATAL: role "vagrant" does not exist, you didn't do step 8 right

Step 11: Testing

bundle exec rails s
sudo service nginx start

http://localhost:3000 should work now
http://localhost:8080 should show nginx welcome page

Step 12: Set up unicorn

####Gemfile

gem 'unicorn'

shell

bundle install
touch <app_root>/config/unicorn.rb

unicorn.rb

root = "/vagrant"
app_name = "YOUR_APP_NAME"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"

listen "/tmp/unicorn.#{app_name}.sock"
worker_processes 4
timeout 40
preload_app true

# Force unicorn to look at the Gemfile in the current_path
# otherwise once we've first started a master process, it
# will always point to the first one it started.
before_exec do |server|
  ENV['BUNDLE_GEMFILE'] = "#{root}/Gemfile"
end

before_fork do |server, worker|
   defined?(ActiveRecord::Base) and
    ActiveRecord::Base.connection.disconnect!
  # Quit the old unicorn process
  old_pid = "#{server.config[:pid]}.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    puts "We've got an old pid and server pid is not the old pid"
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
      puts "killing master process (good thing tm)"
    rescue Errno::ENOENT, Errno::ESRCH
      puts "unicorn master already killed"
    end
  end
end

after_fork do |server, worker|
  port = 5000 + worker.nr
  child_pid = server.config[:pid].sub('.pid', ".#{port}.pid")
  system("echo #{Process.pid} > #{child_pid}")
   defined?(ActiveRecord::Base) and
    ActiveRecord::Base.establish_connection
end
  1. Starting unicorn: bundle exec unicorn -c /vagrant/config/unicorn.rb -p 3000

#Set up Nginx

Step 13: Set up nginx.conf in rails config directory

touch <app_root>/config/nginx.conf

nginx.conf

upstream unicorn {
  server unix:/tmp/unicorn.YOUR_APP_NAME.sock fail_timeout=0;
}

server {
  listen 80 default deferred;
  # server_name example.com;
  root /vagrant/public;
  try_files $uri/index.html $uri @unicorn;
  location @unicorn {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://unicorn;
  }
  
  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  keepalive_timeout 10;
}

Step 14: Symlink nginx.conf

sudo ln -s /vagrant/config/nginx.conf /etc/nginx/sites-enabled/<your_app_name>

Step 15: Delete Nginx default site

sudo rm /etc/nginx/sites-enabled/default

Step 16: Restart Nginx

sudo service nginx restart
  1. Start unicorn: bundle exec unicorn -c /vagrant/config/unicorn.rb
  2. Site should work at http://localhost:8080 now (make sure unicorn is still running)

#Make Unicorn a service for convenience

Step 17: Set up unicorn_init.sh

touch <app_root>/config/unicorn_init.sh
bundle install --binstubs #creates an executable in the bin directory for our unicorn_init shell script

unicorn_init.sh

#!/bin/sh
set -e
# Example init script, this can be used with nginx, too,
# since nginx and unicorn accept the same signals

# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/vagrant
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="$APP_ROOT/bin/unicorn -D -c $APP_ROOT/config/unicorn.rb"
action="$1"
set -u

old_pid="$PID.oldbin"

cd $APP_ROOT || exit 1

sig () {
  test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
  test -s $old_pid && kill -$1 `cat $old_pid`
}

case $action in
start)
  sig 0 && echo >&2 "Already running" && exit 0
  echo "ATTEMPTING TO START UNICORN"
  ruby -v
  $CMD
  # sudo su -c "ruby -v" - postgres
  # sudo su -c "$CMD" - vagrant
  ;;
stop)
  sig QUIT && exit 0
  echo >&2 "Not running"
  ;;
force-stop)
  sig TERM && exit 0
  echo >&2 "Not running"
  ;;
restart|reload)
  sig HUP && echo reloaded OK && exit 0
  echo >&2 "Couldn't reload, starting '$CMD' instead"
  $CMD
  ;;
upgrade)
  if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
  then
    n=$TIMEOUT
    while test -s $old_pid && test $n -ge 0
    do
      printf '.' && sleep 1 && n=$(( $n - 1 ))
    done
    echo

    if test $n -lt 0 && test -s $old_pid
    then
      echo >&2 "$old_pid still exists after $TIMEOUT seconds"
      exit 1
    fi
    exit 0
  fi
  echo >&2 "Couldn't upgrade, starting '$CMD' instead"
  $CMD
  ;;
reopen-logs)
  sig USR1
  ;;
*)
  echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
  exit 1
  ;;
esac

Make unicorn_init.sh executable

chmod +x config/unicorn_init.sh

Step 18: Symlink unicorn_init.sh

sudo ln -s <app_root>/config/unicorn_init.sh /etc/init.d/unicorn_your_app_name

Step 19: Start up Unicorn

#shut down all instances of Unicorn (optional)
ps aux | grep unicorn #find unicorn processes
kill -9 <pid> #shut it down

#start up unicorn as a linux process
sudo service unicorn_your_app_name start #it's important to know which version of ruby will be used when running as different users. if you're facing errors, see below.

#####If you're getting this error:
/opt/vagrant_ruby/lib/ruby/site_ruby/1.8/rubygems.rb:900:in "report_activate_error": Could not find RubyGem unicorn (>= 0) (Gem::LoadError) Your init script isn't using the right version of Ruby. Here are few things you can try:

  1. Modify shell script to run your command as vagrant su -c "$CMD" - vagrant instead of just $CMD
  2. Try sudo service unicorn_app_name start and service unicorn_app_name start

#####To debug effectively:
ONE: Check the version of Ruby that is being used when attempting to boot up Unicorn through your init shell script

#this is just a snippet of code in unicorn_init file
case $action in
start)
  sig 0 && echo >&2 "Already running" && exit 0
  echo "ATTEMPTING TO START UNICORN"
  ruby -v #check the version of ruby you're using
  $CMD
  # sudo su -c "ruby -v" - vagrant #check the version of ruby you're using
  # sudo su -c "$CMD" - vagrant

TWO: Tail Unicorn log output to figure out what other erros you might have

tail -f /vagrant/log/unicorn.log

#####Finally if you're running unicorn as root, you will need to add root as a role for postgres (go through step 8 but add root instead of vagrant)

###Voila! It should work now!

#MISC

VirtualBox slowness issues on mac

  1. Speeding up Vagrant: http://kubaf.wordpress.com/2013/01/12/vagrant-speed-up-on-mac-os-x/
  2. Add following in Vagrantfile
  config.vm.provider :virtualbox do |vb|
    vb.customize ["modifyvm", :id, "--natdnshostresolver1", "on"]
    vb.customize ["modifyvm", :id, "--natdnsproxy1", "on"]
  end

disclaimer: I can't seem to get any of these solutions to speed up my VM. If you managed to speed up your VM on a Mac with VirtualBox, please leave some comment and help us out! Thanks!
In the meantime, you might want to look into VMware instead of VirtualBox

@DidiRaggio
Copy link

Hi Josh, I'm followed these steps to the letter. But after step 18 (Symlink unicorn_init.sh), I try to start unicorn in my VM and I get this:
vagrant@lucid32:~$ sudo service unicorn start
env: /etc/init.d/unicorn: No such file or directory

yet in the previous step I had created said file....
Hopefully you can give me a hand with this issue, all my best. Didi
by the way I'm running on windows 8, perhaps that is the issue.

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