If you run a low volume website, your best value will be shared hosting and the odds are that it will use Cpanel for management. Consider this website, which I host, along with a few others for 2.99 USD per month. My hosting package includes a plethora of features, all of which I can use little to no knowledge of running a webserver. However, effectively using these technology as an experienced user can be a bit more challenging.
I recently deployed a Ruby on Rails application (Redmine) for a website I host. My hosting provider supports Ruby on Rails, but cpanel management makes the deployment workflow a bit different from a dedicated host. When I upgraded to the latest Redmine (1.4.1), I ran into further problems, but I eventually got it working.
The tips in this article were derived from my experience with Redmine, but should apply well beyond just Redmine to any Ruby on Rails application.
To get started, Cpanel provides a Ruby on Rails item in the control panel, allowing you to create a new rails application. Creating an application through this interface will populate
~/rails_apps with a new subdirectory containing your app. This will be a fresh Rails app, but this can be easily deleted and replaced with your own. Note that this process is necessary because it sets up other infrastructure to make it possible to run your Rails app.
Your shared hosting provider should provide a system-wide installation of some common Ruby gems needed for Rails deployment. These can be critical because you likely won’t be able to compile native extensions like mysql yourself.
To add additional gems, you’ll be provided a
~/ruby/gems directory. There is a cpanel interface for this, but you’re better off using the command line via ssh. To install a new gem, you’ll need to set up your environment:
$ export PATH=$PATH:~/ruby/gems/bin $ export GEM_HOME=~/ruby/gems
At this point, you will be able to install gems as normal with the gem command. If you don’t set GEM_HOME, you’ll end up trying to install gems into the system path, which won’t go well.
Working with Bundler
Redmine 1.4.1 switched to using Bundler, which initially made deployment trickier than prior versions. Bundler is intended to make things easier for developers, but it can try to force you to install gems you cannot use. In a shared environment, you are unlikely to be able to build native extensions because of a lack of build tools or development libraries. For native extensions that are required, you’ll need to ask your hosting provider to install it at a system level. For Redmine, this is probably the mysql gem.
Redmine is capable of using other database systems like postresql or sqlite, but it doesn’t require them. If you run bundler as specified, you’ll end up trying to build and install these gems. Fortunately, there is an option to exclude specific gems. For my environment, I used the following command:
$ bundle install --without development test postgresql sqlite rmagick
This command removed development and test requires as recommended by the documentation. It also removes unneeded database gems and the optional rmagick gem. Keep in mind, you ultimately need certain gems to run the application, so this mechanism is only suitable for exlcuding optional gems. If I excluded mysql, for example, I wouldn’t be able to connect to the database, and Redmine would not run.
Starting and Stopping the App
You’ll need to start the app via the cpanel Ruby on Rails control panel. This will start an instance of mongrel and your app will be accessible via some high-numbered port (e.g. 12001). Each app will be deployed in a separate mongrel instance on a different port. If the app does not start, you’ll need to check the mongrel log in
You may find that the control panel gets out of sync with mongrel sometimes and you won’t be able to stop your rails app. In this case, you can check the
~/rails_apps/<app>/log/mongrel.pid and kill this process. Alternatively, search for the mongrel process manually:
$ ps aux | grep mongrel username 1234 0.1 1.1 228588 136644 ? S 00:51 1:41 /usr/bin/ruby /usr/bin/mongrel_rails start -p 12003 -d -e production -P log/mongrel.pid
You can also delete
mongrel.pid to force the control panel to show it in the stopped state.
When you work with gems on the command line, you can set the environment as needed. When Cpanel launches mongrel, however, you have no control over the environment in which it is launched. To set up the environment, you can override the
GEM_HOME variable in
ENV['GEM_HOME'] = '/home/<user>/ruby/gems'
Note: if you use a string literal for the path, you’ll need a fully expanded path, as Ruby will not interpolate ~ to your home directory.
With Redmine 1.4.1, I found that this no longer worked. Redmine had switched to Bundler for gem management. Without bothering to understand all the details, I found a workaround that will reload rubygems with the correct environment:
ENV['GEM_PATH']= "/home/<user>/ruby/gems" require 'rubygems'; Gem.clear_paths; Gem.instance_variable_set(:@searcher, nil)
For Redmine, I found that
config/preinitializer.rb was actually loaded before this, and so I added this code prior to
require 'rubygems' in that file.
If you don’t do this correctly, you’ll see entries in mongrel.log indicating that bundler is required:
Redmine requires Bundler. Please install it with
gem install bundler.
** Daemonized, any open files are closed. Look at log/mongrel.pid and log/mongrel.log for info.
** Starting Mongrel listening at 0.0.0.0:12001
** Starting Rails with production environment...
If you are still having trouble, make sure you do a sanity test with your environment set in the shell. You can start the ruby interpreter (irb) and run
require "rubygems"; require "bundler" to verify they load correctly.
Running Rake Tasks
If you’re lucky, you’ll be able to run the required rake commands on your host. Unfortunately, I wasn’t so lucky with my hosting provider as they appear to enable fork bomb protection in cpanel. The result is a segmentation fault and a stack trace that will give you little clue as to what is going on.
To work around this, you can set up a local environment to run the rake tasks. I used rvm to install the same version of ruby as on my hosting provider on a Linux vm. This method is quite useful as it will let you create multiple Ruby environments to meet your needs without cluttering your system installation.
Use of rvm is fairly self-explanation. Just follow the instructions on the rvm website to install your ruby version:
$ curl -L get.rvm.io | bash -s stable $ source ~/.rvm/scripts/'rvm' $ rvm install 1.8.7
At this point, you have a fresh ruby installation and it will be activated in your path. You can install Redmine again on your local system. Then, run your database migrations locally after modifying your
config/database.yml to point to your remote host. You can allow remote access (restricted to your IP) from the Mysql cpanel pages.