Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HOW TO: Setup Vagrant with VirtualBox running Ruby on Rails 4, Postgres, Unicorn, Nginx, and port forward on OS X 10.10+

I am posting this as a compilation of resources I have come across since the guides out there don't cater for the virtualisation provided by Vagrant and the errors that can occur due to this.

As a result I will reference a lot of material as others have explained and formatted it better than I, while only replacing steps in said material such that it works under Vagrant.

In an actual deployment environment the resources linked are sufficient (this post is only for Vagrant):

  • Vagrant 'Getting Started' documentation (Vagrantup)
  • Installing Ruby via rbenv on Ubuntu 14.04 (DigitalOcean)
  • Deploying with Rails, Unicorn, and Nginx on Ubuntu 14.04 (DigitalOcean)
  • Vagrant Triggers Explanation
  • Working Vagrant Triggers Port Mapping
like image 725
tsujp Avatar asked Sep 14 '25 07:09

tsujp


1 Answers

Step 1: Installing Vagrant, and VirtualBox

You can either use Homebrew or the installers from their respective websites, here and here.

Via Homebrew:

brew cask install vagrant
brew cask install virtualbox

Step 2: Setting up Vagrant

2.2 - First we add a Vagrant Box

Vagrant boxes are images which are cloned into a virtual machine by Vagrant, we're going to use Ubuntu 14.04 from the Ubuntu repository to create our virtual machine.

vagrant box add ubuntu/trusty64

2.3 - Second we initialise our Vagrantfile

Vagrants default provider for virtualisation is VirtualBox so we don't have to configure anything on that front. All we need do is create a Vagrantfile in or next to our projects root folder.

  • The Vagrantfile will serve as the 'location marker' for the /vagrant folder inside our virtual machine, as such anything contained in same folder as our Vagrantfile will be accessible inside our virtual machine.

Make sure you cd to wherever you want your shared directory to start from, then all we need to do is run vagrant init with our Ubuntu 14.04 image.

vagrant init ubuntu/trusty64

Ta-da! We're now ready to boot and play around in our virtual machine.


Step 3: Booting and SSH-ing into our virtual machine

To boot and then ssh into our virtual machine we must be in the same directory as our Vagrantfile and run:

vagrant up
vagrant ssh

Now we're ready to install Ruby, Postgres, Rails, Unicorn, and Nginx.


Step 4: Installing packages and configuring our virtual machine

4.1 - Follow DigitalOcean's guide on installing Ruby and Ruby on Rails until you reach the optional steps, here.

  • Note: Under Install rbenv be sure to leave the file as .bash_profile.

4.2 - Install Postgresql on Ubuntu 14.04.

Update apt-get, then install Postgres and it's dependencies:

sudo apt-get update
sudo apt-get install postgresql postgresql-contrib libpq-dev

4.3 - Follow DigitalOcean's guide on deployment setup with Rails, Unicorn, and Nginx, here.

  • Note: Make sure to reference the below points on which things to replace.
  • Prior to the first step make sure you are in your shared directory, that way you can access the files from your host machine, e.g. to use Sublime Text. To access your shared directory enter cd /vagrant.
  • Configure Unicorn: Copy the supplied configuration, however "Unicorn can't store a .sock file on a Virtual Box Shared Folder, so what you'll have to do is modify for the socket [...]" solution at source. Thanks to Dave Long.
  • Create Unicorn Init Script: Copy the supplied configuration, however:

    1. The USER will always be vagrant.
    2. We have to remove the /home from the APP_ROOT path also leaving just /$USER/$APP_NAME.
    3. Finally, given that ports under 1024 are privileged on OS X we're going to have to force our Rails application run on something higher so,:

      • Change this line: CMD="cd $APP_ROOT && bundle exec unicorn -c config/unicorn.rb -E $ENV -D"
      • Into this: CMD="cd $APP_ROOT && bundle exec unicorn -c config/unicorn.rb -E $ENV -D -l 0.0.0.0:3000"
  • Install and Configure Nginx: Copy the supplied configuration, however:
    1. We changed the path to the unicorn.sock so amend the path to server unix:/home/tmp/sockets/unicorn.sock fail_timeout=0;.
    2. At the root configuration line make sure you replace deploy for with vagrant, producing: root /home/vagrant/APPNAME/public;.
    3. Finally, Nginx may not be able to find the Rails application correctly so we're going to set the proxy_pass to http://localhost:3000;.

Since we changed the unicorn.sock file location to a folder inside the /tmp/ folder (which will be deleted every time we halt our machine) we're going to have to setup some shell provisions via vagrant to make sure that the command mkdir /tmp/sockets is run every time we issue vagrant up for this project.

Simply add the following under the configure block in your Vagrantfile anywhere you like:

# Create the /tmp/sockets folder we need every time on startup
  config.vm.provision "shell",
    inline: "mkdir /tmp/sockets"

Now instead of vagrant up we must run vagrant up --provision every time we wish to start our virtual machine and the /tmp/sockets folder will be created for us. If we don't make this folder Unicorn won't start!


Step 5: Port forwarding

At this point you should be able to access your application on http://localhost:8080/tasks provided you have port forwarding setup in your Vagrantfile. Really though, you'll never access your application with a port on the end so we need to port forward and fix this. As mentioned before Vagrant cannot forward to ports under 1024 as they are privileged on OS X. Running Vagrant under sudo does not help either as this privilege isn't passed to the VBoxManage process Vagrant spawns.

As a result we will forward ports 8080 on our host machine to the virtual machine (guest)'s port 80. Similarly, we shall forward all traffic on port 80 of our host machine to port 8080, also on our host machine. This way we can:

  1. Access localhost on our host machine...
  2. Which will forward to localhost:8080 on our host machine...
  3. Which will be forwarded to localhost:80 on our guest (virtual) machine.

Finally, we can access our application as localhost!

5.1 - Port forwarding host to guest

The host machine is your physical machine, the guest is your virtual one. We're going to port forward port 8080 on the host to 80 on the guest.

  1. Open your Vagrantfile and find the line with config.vm.network in it.
  2. Uncomment the line and edit it, whilst adding another, such that it resembles:

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

You've now port forwarded the default http and https ports from your host machine to the guest.

5.2 - Port mapping the host's ports

Now we need to map the host's ports 80 and 443 to the host's ports 8080 and 8443 so that vagrant can pick it up! We're going to use a nifty plugin called vagrant-triggers because we don't want this taking place all the time. We only need it when we're running our Vagrant instance. Using vagrant-triggers will allow us to execute commands on the host according to commands Vagrant is told to execute.

  1. Install vagrant-triggers by executing the following command in the same directory as your Vagrantfile: vagrant plugin install vagrant-triggers.
  2. Paste the following configuration into your Vagrantfile inside the configure block, anywhere you like.

config.trigger.after [:provision, :up, :reload] do
  system('echo "
    rdr pass inet proto tcp from any to any port 80 -> 127.0.0.1 port 8080  
    rdr pass inet proto tcp from any to any port 443 -> 127.0.0.1 port 8443  
    " | sudo pfctl -ef - > /dev/null 2>&1; echo "==> Fowarding Ports: 80 -> 8080, 443 -> 8443"')  
end

config.trigger.after [:halt, :destroy] do
  system("sudo pfctl -ef /etc/pf.conf > /dev/null 2>&1; echo '==> Removing Port Forwarding'")
end

Thus, running vagrant up will setup the port mapping and running vagrant halt will remove it.

You can now access your application from http://localhost/tasks.

I hope this guide helps everyone. I had to go through a bit of hassle finding out information as to why this wasn't working under Vagrant specifically.

Huge thanks to DigitalOcean and this community whose members provided content for this guide including links to the pfctl mapping under Yosemite and Unicorn socket configurations.

If you find anything wrong with the guide please message me so I can investigate and amend if required!

Happy coding,

tsujp

like image 150
tsujp Avatar answered Sep 17 '25 00:09

tsujp