Last Updated: December 31, 2020
·
6.468K
· joshteng

Setting Up Vagrant

This was written with Github flavored markdown.. It messes up here a little. See a better version: https://gist.github.com/joshteng/9901155

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
```sh

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
sh 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 ruby 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

2 Responses
Add your response

nice post, thanks

over 1 year ago ·

You're welcome!

over 1 year ago ·