I’ve had a lot of people asking for help setting up a Ruby on Rails environment recently so figured I’d put a post together detailing how I set up my boxes.
This won’t be a guide for everyone, but it’s a tried and tested setup that not only performs well, but is also well set up to work with most gems and the development tools you’ll want. This guide covers both development and production environment setups.
Step 0 – Operating System
Okay, first off- this is a guide for Linux. I never got along with Mac platforms, and I certainly wouldn’t use Windows for development these days. I strongly recommend that you use Linux.
If you’re using Windows or Mac, then I would advise setting up a virtual machine using VirtualBox – grab Ubuntu 10.04 Server, set it up in a little VM, and you’ve got yourself a home-grown VPS on which to develop. It’s not a lot of time and effort, and the result is a much nicer experience overall. Use PuTTY to communicate with the VM, grab a tool like Expandrive or set up Samba to share your home folder on your VM, and you can still work from Windows – you just use a Linux environment and shell.
I use a similar set up – I have physical servers in a rack alongside my desktop which run my development, staging and other test environments. I work on my desktop, which runs Windows 7, using the e text editor, and PuTTY to talk to the Linux boxes. Having previously tried Mac and an all-Windows setup, this is by far the best setup I’ve come up with.
Step 1 – Ruby
I always set up Ruby from source. It’s trivial to do, trivial to update, and you get all the benefits of from-source installation. You’ll have to have installed a compiler, of course – on Ubuntu, this is in the build-essentials package.
Go to the Ruby download page and grab the latest version, or just grab the stable tarball:
wget ftp://ftp.ruby-lang.org//pub/ruby/ruby-1.9-stable.tar.gz tar zxf ruby-1.9-stable.tar.gz cd ruby*
# Now compile and install Ruby: ./configure make sudo make install ruby -v
# Ensure we've got the latest rubygems package sudo gem update --system
Now we’ve got Ruby installed, with the latest rubygems – great! We’re half-way there already.
Step 2 – Databases
Most people wanting to work with Ruby/Rails will be doing web work. This overwhelmingly involves a relational database server. So, let’s crack on! I strongly recommend PostgreSQL over MySQL these days, and use it for all my projects. I’ve even transitioned older projects to PostgreSQL.
There’s plenty of how-to guides out there, but really all you need to do on Debian is install, and configure. We’ll also install the development libraries so we can compile gems against it.
sudo apt-get install postgresql postgresql-client libpq-dev sudo gem install pg # Recommended gem for postgresql access sudo nano -w /etc/postgresql/8.4/main/pg_hba.conf
You shouldn’t need to adjust anything in the postgresql.conf file, except potentially enabling networking by setting
listen_addresses = '*'
However, you probably want to be able to log into your postgresql server from other machines on your LAN, or if you’re using a VM, from your host OS. This will let you use tools like pgAdmin to visually set up your DB, which can save time and help newer users. This is why we’re now editing the pg_hba.conf file – this is PostgreSQL’s host based authentication system. By default you can log in by ident authentication- if you _are_ the unix user equivalent of the user you’re logging in as, you need no password (So if you want to do anything system-wise, sudo su postgres and go from there!). Let’s enable logging in using a password from anywhere on the local network 192.168.0.0/24 (the whole subnet):
host all all 192.168.0.0/24 md5
Adding that line is all you need to do.
MySQL is very similar – just remember to install the libmysql-dev package as well! You may need to adjust settings in my.cnf to enable networking, depending on your system’s defaults.
Step 3 – Development Server
Well, let’s see if we can use some of all this. Let’s set up a app with Sinatra, and run it.
sudo gem install sinatra thin nano -w helloworld.rb
Now put this in helloworld.rb, and save it.
require 'rubygems' require 'sinatra' get '/hi' do "Hello World!" end
Now run helloworld.rb
ruby helloworld.rb
Voila! Open your browser, go to the URL indicated by the output of that script, and bask in your first “Hello World” web application.
Step 4 – Web Server
At this point you can choose to stop, and work without a web server, or you can choose to set up something closer to a production environment to develop on. Maybe you _are_ on your production machine!
Nginx (pronounced engine-x) is an excellent web server with a memory footprint of about 10MB and fantastic scalability, and you’ve got to have a _very_ good reason to have to use Apache these days in my mind. Not to mention that nginx has a much nicer configuration file format, is faster, and works awesomely for pretty damn near every task you throw at it.
I tend not to use Passenger with nginx, but it is an option. I find it much more reliable and more flexible to run my own application servers, and have nginx just proxy to them. I use unicorn to run the application server cluster, and socket communications to handle proxy communications.
Let’s install nginx and unicorn (again, we’ll be installing nginx from source, though this time for flexibility):
wget http://nginx.org/download/nginx-0.7.67.tar.gz tar zxf nginx-* cd nginx-*
# See what options there are with --help on ./configure - I've listed my recommendations. You may need to install libpcre3-dev. ./configure --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --with-pcre --with-http_ssl_module make sudo make install
sudo nano -w /usr/local/nginx/conf/nginx.conf
Now you can configure your web server to your heart’s content – once you’re done, run
sudo /usr/local/nginx/sbin/nginx # to start
sudo killall nginx # to stop
Here’s an example configuration to have a website proxy to a backend unicorn cluster, using a socket for communications:
upstream emunicorn {
server unix:/opt/www/evemetrics/shared/evemetrics.sock fail_timeout=0;
}
server {
listen 80;
server_name daedalus.local;
root /home/james/evemetrics/public;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain application/xml text/css application/javascript application/x-javascript;
gzip_disable msie6;
error_page 500 502 503 504 /50x.html;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
# Magic for using asset fingerprinting in Rails via md5 of resource
rewrite "(.*)-fp-[0-9a-z]{32}(.*)" $1$2 last;
location ~* (css|js|png|jpe?g|gif|ico)$ {
access_log off;
add_header Cache-Control public;
expires max;
}
if (-f $document_root/system/maintenance.html){
rewrite ^(.*)$ /system/maintenance.html break;
}
location / {
if (-f $request_filename/index.html) {
rewrite (.*) $1/index.html break;
}
if (-f $request_filename.html) {
rewrite (.*) $1.html break;
}
# Anything not matching the two above - which are basically page cache support tricks - we proxy to unicorn.
# We also check to see if we're trying to serve a file that exists - this stops unicorn handling static assets.
if (!-f $request_filename) {
proxy_pass http://emunicorn;
break;
}
}
location = /50x.html {
}
}
Of course, we need a unicorn.rb configuration file for our app to define that socket, and then we can run unicorn or unicorn_rails with the -c option to specify the config file.
We use this config file on EVE Metrics for production:
worker_processes 4 listen '/opt/www/evemetrics/shared/evemetrics.sock', :backlog => 2048 stderr_path "/opt/www/evemetrics/shared/log/unicorn.stderr.log" stdout_path "/opt/www/evemetrics/shared/log/unicorn.stdout.log" timeout 120
Obviously adjust for paths, and you’re good to go. You may also want to tweak the number of workers- this should usually be the number of cores your machine has. Nginx also has a similar setting in nginx.conf.