Tutorial

How to set up a Debian 9.3 Ruby on Rails Development VM on Windows 10 with Vagrant, Virtualbox, RVM, MariaDB, Apache and Phusion Passenger

What is this tutorial about? The goal is to set up a virtual machine (VM) for Ruby on Rails development on a Windows 10 based host. The final VM will not have any GUI or actual development tools, only the technologies required to run Ruby on Rails and to access the VM via SSH. This is somewhat closer to an actual production environment than installing the technologies on the host machine. And in my experience being closer to the production environment helps a lot. I wont go into details about that in this post, because you are probably here to read some instructions to help you with your current problem and move on. ūüôā

The following technologies will be used within this tutorial:

  • Windows 10 x64 (as host machine)
  • Debian 9.3.0 (x64, Stretch, as virtual machine)¬†download
  • Oracle Virtualbox 5.2.4¬†download
  • Vagrant 2.0.1¬†download
  • MariaDB 10.1 (Database)
  • Apache 2.4 (Webserver)
  • RVM (Ruby Version Manager)
  • Ruby 2.5.0-rc1
  • Phusion Passenger¬†5.1.12
  • Rails 5.2-beta2

You might ask yourself: Why are we using a release candidate and a beta version? Why not stable releases? Well in this case I am going to use this VM to build a new application that is going to take a while to complete and go into production. Until then Ruby on Rails 5.2 and Ruby 2.5 are probably going to be released, I have no need to update then and can take advantage of the new features those releases are providing during development.

Another question you might have is: Why are we using Phusion Passenger with the Apache webserver and not something like Webrick or Puma? This setup allows to run multiple Ruby on Rails (and other technologies, like PHP) applications within a single server instance, without the need to define different ports for each app. You just open up your browser, enter http://your-hostname, your project folder pops up, you navigate to your app and voilà: it boots up. For more information on how Passenger handles this, visit their documentation.

Step 1: Building a Debian Vagrant base box

This first step is going to show you how to set up a Debian base box with Vagrant. The box can be used as a basic platform in Vagrant to actually set up your development environment. At its core it is just going to be a Debian installation with a SSH Server running and the Virtualbox Linux Extensions installed (which allow to use features like shared folders between host machine and VM).

You can skip this if you want and just move on to step 2. The Vagrant VM you will set up in step 2 will download the complete box from this (just-another-developer-blog.com) server automatically. The only thing you have to do from the current step is to download¬†and install Vagrant and Virtualbox. But if you don’t trust me that I set up the base box correctly (I did it most certainly not optimally), than read on.

Download and install the tools

First of all download Vagrant, Virtualbox and a Debian 9.3 Netinstall ISO file. Then install Vagrant and Virtualbox. To check if Vagrant is correctly installed open up your Windows command prompt and enter:

vagrant -v

 

This should return the version you have installed (It should be 2.0.1 if you are not using a more recent version). All good!

Then open up Virtualbox. It opens up? Good! If not, try to open it as admin, although it should work without that.

Creating the new virtual machine

We will now create a new virtual machine in Virtualbox. Therefor boot up Virtualbox and follow these:

  1. Click on New in the top left corner.
  2. You should be asked for a name. Enter debian9 and this should automatically set the type to linux and the version to debian (64 Bit).
  3. Leave the memory size as it is, you will be changing this later when you boot up your Vagrant VM and can adjust it whenever you want.
  4. When asked for a hard disk select create a virtual hard disk now and use the VDI format.
  5. For the storage on physical hard disk mode I usually go with dynamically allocated.¬†This takes up space as is required by the VM and does not reserve it up front. But it will be a bit slower than the fixed size mode. Virtualbox describes the advantages for each mode quite well when you need to select it. How much faster is the fixed mode? I couldn’t tell you because I never tried to measure the difference.
  6. Next you will be asked about the actual disk size and location. Chose a location to your liking. If you chose a dynamically allocated disk in the previous step you can bump up the disk space quite a bit without actually using it. If you use fixed allocation you need the physical disk space right now. Whatever method you choose: Be aware that you plan for enough space later on in development. Although it is possible to increase the size for a disk later on, it is a bit of a hassle and wastes time. In this instance I set my size to 80GB.
  7. You should now see a new VM in Virtualbox, well done!

Installing Debian on the virtual machine

We will now install Debian on this brand new VM. Follow these:

  1. Select the VM and hit Start
  2. You should be asked to insert a start up disk, select the Debian 9 Netinstall ISO file you downloaded earlier and proceed.
  3. Select Graphical Install
  4. You will be asked about your language, country and keyboard style. Chose to your liking. For the following steps I will assume you picked English as your language.
  5. Set the hostname to debian9
  6. You can leave the requested domainname empty
  7. Set the root password to vagrant
  8. When asked about the user, enter vagrant for fullname, username and password
  9. Set partition disks to guided – use entire disk
  10. Select the disk given, there should only be one disk to chose from
  11. Select all files in one partition
  12. Select finish partitioning and write changes to disk
  13. Confirm “Write changes do disks” with Yes
  14. When asked to scan another CD or DVD, select No
  15. Configure the package manager to your liking. In the provided sample box I selected a package repository located in Germany and disabled any usage tracking.
  16. When asked which software to install select SSH Server only
  17. Answer “Install the GRUB boot loader to the master boot record?” with Yes
  18. Pick /dev/sda as “Device for boot loader”
  19. Finish by restarting the VM when asked.

Configuring Debian virtual machine to work with vagrant

Now we are going to configure Debian on our virtual machine in order to allow Vagrant to interact with the machine later on.

First you have to login as root with the password vagrant. Afterwards execute the following script:

# update the package repository information
apt update

# installing some packages that allow us to execute the following commands
apt -y install build-essential module-assistant sudo curl dkms linux-headers-`uname -r`
m-a prepare

# allowing the vagrant user to access via ssh and no password
mkdir /home/vagrant/.ssh
cd /home/vagrant/.ssh
curl https://raw.githubusercontent.com/mitchellh/vagrant/master/keys/vagrant.pub > authorized_keys
chmod -Rf 700 /home/vagrant/.ssh
chown -Rf vagrant:vagrant /home/vagrant/.ssh
echo "%admin ALL=NOPASSWD:ALL" > /etc/sudoers.d/admin
groupadd admin
usermod -G admin vagrant

 

Now you have to access a menu from the Virtualbox window your VM is running in. Access Devices and click on Insert Guest Additions CD Image. There is no feedback after the click, but that is ok. Return to the VM command line interface and enter the final commands:

mount /media/cdrom
sh /media/cdrom/VBoxLinuxAdditions.run
shutdown -h now

 

Your VM window should close and you are done here.

Building the Vagrant Box

We have our complete base Debian VM. What we need to do now is building a Vagrant Box from it. To do so open up your Windows command prompt (cmd) and enter:

vagrant package --base debian9 --output c:/path/to/my/debian9.box
vagrant box add debian9 c:/path/to/my/debian9.box

 

Chose the file name and path to your liking. After executing these commands you can remove the VM you built in Virtualbox – you don’t need it anymore.

You can upload the newly created debian9.box file to a fileserver you might own or just keep it somewhere on your hard drive. We have already added the box to Vagrant and technically do not need the debian9.box file anymore. I would recommend to keep it somewhere on a file server in order to access it from wherever you want. Vagrant can dynamically download and add boxes from remote servers – which is pretty convenient.

Testing your VM

If you want to, you can execute a quick test if your box works. To do so, follow these:

  1. Create a test folder somewhere on your drive
  2. Boot the Windows command prompt (cmd)
  3. Navigate to the test folder
  4. Enter vagrant init debian9 –¬†¬†a Vagrantfile should appear in your test folder
  5. Enter vagrant up and wait a while, no errors should occur
  6. Enter vagrant ssh and you should be connected to your VM via SSH
  7. Enter exit and you should be back within your windows command prompt
  8. Congratulations, everything works fine!
  9. Enter vagrant destroy -f in order to remove the created VM
  10. Remove the test folder and pad yourself on your shoulder for the test effort

Step 2: Setting up the Vagrant project

Finally we are ready to build out development environment. You need to create two folders for that. One folder contains the meta data for the Vagrant VM. I chose D:/dev-vm for this, but you can change it to your liking. The other folder will contain the actual projects. You might already have a folder with some Ruby on Rails projects in it; that is ok, you can use that one. I created the folder D:/projects.

After you have created those folders open up your Windows command prompt (cmd) and navigate to the folder of the development VM (D:/dev-vm). Enter vagrant init debian9 and a Vagrantfile should have been created within the folder. Open up a file editor and replace the content with this script:

# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
	# reference to our debian 9 box
	config.vm.box = "debian9"

	# download path, if the box is not installed on the current host machine
	# you can remove this line, change the URL to your server or leave it as is
	config.vm.box_url = "https://just-another-developer-blog.com/wp-febac-content/uploads/2017/12/debian9.3.0_vagrant2.0.1_vbox5.2.4.box"

	# setting the hostname of the vm to the hostname of the host system plus a "-vm" suffix
	# not necessary, but helpful on some occasions
	config.vm.hostname = "#{ENV['COMPUTERNAME'] || `hostname`[0..-2]}-vm".downcase

	# setting a fixed ip for the vm to access from the host (this ip is not available outside of the host system)
	config.vm.network :private_network, ip: "10.101.10.10"

	# setting up port forwarding for http and https
	# not necessary, only if you want to access apache2 server with the name of the host machine
	config.vm.network :forwarded_port, guest: 80, host: 80
	config.vm.network :forwarded_port, guest: 443, host: 443

	# setting up port forwarding for mariadb database
	# not necessary, only if you want to access mariadb database with the name of the host machine
	config.vm.network :forwarded_port, guest: 3306, host: 3306

	# adding a synced folder in order to exchange your projects between vm and host
	# you have to adjust the first parameter to the folder name where your projects are located
	config.vm.synced_folder "D:/projects", "/var/www"

	config.vm.provider :virtualbox do |vb|
		# configuring the vm name by adding timestamp
		vb.name = "dev-vm_#{Time.now.strftime('%Y-%m-%d_%H-%M-%S')}"

		#  customizing the assigned memory, right now it is set to 1024MB
		vb.customize ["modifyvm", :id, "--memory", "1024"]

		#  customizing the assigned cpu usage cap in percent, right now it is set to 100%
		vb.customize ["modifyvm", :id, "--cpuexecutioncap", "100"]

		#  customizing the assigned cpu cores, right now it is set to 2
		vb.customize ["modifyvm", :id, "--cpus", "2"]

		# some other configurations to let your vm run faster and disabling useless stuff
		vb.customize ["modifyvm", :id, "--pae", "on"]
		vb.customize ["modifyvm", :id, "--audio", "none"]
		vb.customize ["modifyvm", :id, "--usb", "off"]
		vb.customize ["modifyvm", :id, "--mouse", "ps2"]
	end

	config.vm.provision :shell, path: 'setup.sh'

	# uncomment this if you want to setup a new project
	#config.vm.provision :shell, path: 'new_project.sh'

	# uncomment this if you want to use an existing project
	#config.vm.provision :shell, path: 'existing_project.sh'
end

 

You will also need no create three more files within the development VM folder (D:/dev-vm).

Create the file D:/dev-vm/setup.sh and insert the following content:

#!/usr/bin/env bash

# act as root
sudo -s

# updating the debian package repository
apt update

# allowing root user to access vm via ssh client
echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
/etc/init.d/ssh restart

# installing the mariadb server
# the default-libmysqlclient-dev package is required to make the mysql2 ruby gem to work
apt -y install mariadb-server default-libmysqlclient-dev

# a mariadb root user is created with full access from every host/ip
mariadb -e "UPDATE mysql.user SET plugin = '' WHERE user = 'root' AND host = 'localhost';"
mariadb -e "CREATE USER 'root'@'%';"
mariadb -e "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;"
mariadb -e "FLUSH PRIVILEGES;"

# configuring mariadb to allow access from every remote host/ip
echo "bind-address = 0.0.0.0" >> /etc/mysql/mariadb.conf.d/50-server.cnf

# restarting mariadb service in order to take changes into effect
/etc/init.d/mysql restart

# install the apache server
apt -y install apache2

# removing some sample configuration
rm -f /etc/apache2/sites-enabled/000-default.conf
rm -Rf /var/www/html

# restarting apache service
/etc/init.d/apache2 restart

# installing RVM
curl -L https://get.rvm.io | bash
source /etc/profile.d/rvm.sh
rvm requirements

# installing ruby and bundler gem
rvm install 2.5.0-rc1
rvm use 2.5.0-rc1 --default --create
gem install bundler -v 1.16.0

# installing passenger gem
gem install passenger -v 5.1.12

# installing and configuring the passenger apache module
apt -y install libcurl4-openssl-dev apache2-dev libapr1-dev libaprutil1-dev
passenger-install-apache2-module -a
cp /vagrant/passenger/passenger.load /etc/apache2/mods-available
ln -s /etc/apache2/mods-available/passenger.load /etc/apache2/mods-enabled
cp -f /vagrant/passenger/default.conf /etc/apache2/sites-available
ln -s /etc/apache2/sites-available/default.conf /etc/apache2/sites-enabled

# creating a directory in order to allow multiple rails apps to be mounted into the apache
mkdir -p /root/racks

# restarting apache service in order to take changes into effect
/etc/init.d/apache2 restart

 

Create the file D:/dev-vm/new_project.sh and insert the following content:

#!/usr/bin/env bash

# installing nodejs javascript engine because rails needs it in order to run with the default
# configuration and does not install it by default
apt -y install nodejs

# installing the rails gem
gem install rails -v 5.2.0.beta2

# moving to project folder and initializing new rails project
cd /var/www
rails new my_project -d mysql

# mounting new passenger app endpoint into the apache server and restarting apache
echo "PassengerBaseURI /my_project/public" > /root/racks/my_project
/etc/init.d/apache2 restart

# creating development and test database for the sample app
mariadb -e "CREATE DATABASE IF NOT EXISTS my_project_development CHARACTER SET utf8 COLLATE utf8_unicode_ci;"
mariadb -e "CREATE DATABASE IF NOT EXISTS my_project_test CHARACTER SET utf8 COLLATE utf8_unicode_ci;"

# executing rails migrations
cd /var/www/my_project
rails db:migrate

 

Create the file D:/dev-vm/existing_project.sh and insert the following content:

#!/usr/bin/env bash

# mounting public folder of the existing project to the passenger and restarting apache
echo "PassengerBaseURI /existing_project/public" > /root/racks/existing_project
/etc/init.d/apache2 restart

# execute bundle install
cd /var/www/existing_project
bundle install

# creating development and test database for your existing project
mariadb -e "CREATE DATABASE IF NOT EXISTS existing_project_development CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
mariadb -e "CREATE DATABASE IF NOT EXISTS existing_project_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"

# running rails migrations
rails db:migrate

 

You are still not ready. One subfolder and two more files to go. Create a subfolder named passenger within your development VM folder (D:/dev-vm/passenger). Within this folder place two more files.

Create the file D:/dev-vm/passenger/default.conf and insert the following content:

<VirtualHost *:80>
	ServerAdmin webmaster@localhost
	DocumentRoot /var/www

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

	RackEnv development

	Include /root/racks
</VirtualHost>

 

Create the file D:/dev-vm/passenger/passenger.load and insert the following content:

LoadModule passenger_module /usr/local/rvm/gems/ruby-2.5.0-rc1/gems/passenger-5.1.12/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
	PassengerRoot /usr/local/rvm/gems/ruby-2.5.0-rc1/gems/passenger-5.1.12
	PassengerDefaultRuby /usr/local/rvm/gems/ruby-2.5.0-rc1/wrappers/ruby
</IfModule>

 

Each script I provided here is documented and you should read through them one by one to understand what is going on and reconfigure some lines in order to make it work with your desired setup. When you have done all this return to your Windows command prompt, enter vagrant up and watch (this can take some minutes, so might want to grab a coffee in the meantime).

After your well deserved break you can check if the build process went through without any errors. If not, let me know in the comments and I will see if I can help.

Step 3: Checking if everything works and how to use it

So we have built a development VM with everything installed to run Ruby on Rails with MariaDB. What now? How can you actually check if everything works as intended? How can you access your Database or your Rails project?

Starting and stopping the VM

First of all you need to know how to start and stop your development VM. For this you have to use your Windows command prompt (cmd), navigate to the development VM folder (D:/dev-vm). To stop the VM enter vagrant halt, to start it enter¬†vagrant up. For more information on how to use Vagrant I refer you to the documentation of Vagrant’s CLI.

Access your VM via SSH

First of all you should check if you can actually connect to you virtual machine. Your virtual machine should respond to the SSH port 22 when you access it via the IP 10.101.10.10. Vagrant also automatically establishes a port forwarding for SSH. If you only run one VM at a time this port should usually be 2222. This means you should be able to connect via SSH by using the name of your host machine and this port: name_of_my_machine:2222

A good tool to test this with is PuTTY, which you can download here. When connecting the first time you might be asked to trust this connection. Afterwards you can login with the root user and the password vagrant.

Access MariaDB

If you want to access your MariaDB directly (and you probably want to do so at some point in time) you can do this through different ways.

The simplest way would be to connect to your VM via SSH and enter mariadb. The MariaDB command line interface should pop up and you are good to go.

Since the command line interface is not very comfortable you could connect through a separate client application like Navicat. You can connect through the IP 10.101.10.10 with the default port 3306 or you can enter your host machine name and use the same port (unless you did disable the port forwarding in the Vagrantfile or change it up).

Access the Apache Webserver

This is easy: Click http://10.101.10.10 or open up your favourite development browser and enter the URL manually. You should see a directory listing where the content of your projects folders can be seen. By default you should not be able to connect via https, this needs to be configured first. Maybe I will write another post about this topic in the future.

If you kept the port forwarding line (80 and 443) in the Vagrantfile you can also access the webserver via http://your_hostname (and so can anyone in your local network if there is no firewall, keep that in mind!).

Open your Ruby on Rails Application

If you want to access your application from within your browser you need to navigate to the public folder within your project directory and the Ruby on Rails welcome screen should show. If you have created a new app via the provided script this should work with http://10.101.10.10/my_project/public.

If any error is shown instead of the welcome screen, follow the instructions Rails is giving you to fix the problem or leave a comment so that I may help you.

If you want to access your Rails project via the Rails console you have to access your VM via SSH first. There you have to navigate to the folder /var/www. Check the content of the directory – it should be the content of your projects folder. From there you can navigate into your desired project folder and launch the Rails console, the migrations or test suite.

Step 4: Go develop something & send some feedback ūüôā

This is my first extensive blog post on a technical topic. So if you have any feedback regarding the content or the presentation, please let me know!

Leave a Reply

Your email address will not be published. Required fields are marked *