Posts tagged Server

With the release of Ubuntu 12.04 LTS the time has come - albeit late - for an updated guide on rolling your own Rails production server with a long term supported Ubuntu. This is an updated guide based on an earlier version for Ubuntu 11.10. Most steps are the same, but I've gone through the guide in a fresh virtual machine to make sure everything still works as expected.

The setup

I'm assuming that you already have installed Ubuntu Server with a non-root user (with sudo rights) and that SSH is setup (e.g. like this.) Use sudo, do not use root, because this is a requirement for rvm. You should setup your hosts file for your server. For example, my server has these settings:

# /etc/hosts
127.0.0.1   localhost
12.34.56.78 server.web-l.nl

# /etc/hostname
server.web-l.nl

This should prevent any messages from apache saying that it could not determine a fully qualified domain name (fqdn).

Installing Apache

We'll be installing apache using apt-get. You'll need sudo to do this. While we are installing apache, we might just as well include some packages needed later on.

sudo apt-get install apache2 curl git build-essential zlibc zlib1g-dev zlib1g libcurl4-openssl-dev libssl-dev libopenssl-ruby apache2-prefork-dev libapr1-dev libaprutil1-dev libreadline6 libreadline6-dev

Now redirect your web browser to your servers IP. It should show a welcome page saying: "It works!". Good, you've got your basic webserver working!

Installing MySQL

Up next is MySQL, our database server. Install MySQL via apt-get, the installer will ask you for a database root password, make sure to pick a strong password.

sudo apt-get install mysql-server

Let's also create a mysql user and database for your rails app. I'm naming both the database and the user myrailsapp, but you are free to pick any name you feel appropriate. Make sure the replace the password (correcthorsebatterystaple) with something more secure. You can open the mysql console using:

mysql -u root -p

Run the following commands to create a user and database:

create database myrailsapp;
grant all privileges on myrailsapp.* to 'myrailsapp'@'localhost' identified by 'correcthorsebatterystaple'; 
flush privileges;
exit;

You can verify the account by running mysql -u myrailsapp -p

Installing RVM and Ruby

We'll get to setting up passenger later, but lets first install RVM and a recent ruby. We'll use the curl command provided on the RVM website. Note: This is the first and only time you will use sudo to do something with RVM. After this, a user just needs to be in the RVM group to use rvm. If, for any reason, sudo is needed to do something with rvm, you have to use rvmsudo instead of sudo.

*Note: * The installation script changed location recently, the new command is:

curl -L https://get.rvm.io | sudo bash -s stable

This should successfully install RVM in a multi-user install. The next step is to add yourself to the RVM user group. Let's also add a deploy user with RVM access while we're doing this. Add yourself to the deploy group for convenience.
Also set the permissions on the /var/www folder. With these settings every user in the deploy group can modify files in the /var/www folder. Then, log out of your terminal/shell and log back in. (This will update your group permissions)

sudo adduser deploy
sudo adduser deploy rvm
sudo adduser <yourusername> deploy
sudo adduser <yourusername> rvm
sudo chown -R deploy:deploy /var/www
sudo chmod g+w /var/www

Time to install a Ruby. You can check the list of available rubies using rvm list known. I have chosen to install ruby 1.9.3-p194, which is the latest stable version at the time of writing. Compilation can take some time, so get a caffeinated beverage of your choice while your server is busy compiling. Then, set the new ruby to be your default ruby and install bundler. (We'll need bundler later)

RVM, Bundler, Rubygems and management

Basically, there are several good ways to manage our gems with respect to Passenger. There also is a wrong way, which is to install all gems in the same rvm gemset on the same server. Good options (in my opinion):

  1. Use a single server for each application. This gives the best separation and redundancy, but it can cost you, especially if you have a lot of low traffic apps.
  2. Using a separate rvm gemset for each application. There are some really good instructions on how to do this in this blog post by Darcy.
  3. Use Bundlers --deployment option to install all gems into the shared/bundle directory of your app. This is what we'll use in this guide, because it does not require any extra setup (unlike option 2), while it provides full separation of gems for your apps

You ask why I've installed RVM even if I'm going with option 3??? Well, this is because rvm makes it easier to install and maintain your desired ruby on Ubuntu. The apt packages for ruby are usually a bit behind.

Installing Phusion Passenger

Start by installing passenger. Then install passenger-install-apache2-module using rvmsudo. The module installer is very helpful and will give you directions if anything is missing.

gem install passenger
rvmsudo passenger-install-apache2-module

In the end, the installer will show how to configure the apache configuration files in order to use passenger. Instead of editing the configuration files directly, we'll place 2 files (passenger.load and passenger.conf) in /etc/apache2/mods-available, so that we can use a2enmod and a2dismod to active/deactivate the module. Use sudo to edit these files. The contents below are indicative, use what the passenger installer will return to you. (most likely, the version numbers will change with time)

#/etc/apache2/mods-available/passenger.load
LoadModule passenger_module /usr/local/rvm/gems/ruby-1.9.3-p194/gems/passenger-3.0.15/ext/apache2/mod_passenger.so

#/etc/apache2/mods-available/passenger.conf
PassengerRoot /usr/local/rvm/gems/ruby-1.9.3-p194/gems/passenger-3.0.15
PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.3-p194/ruby

Activate the passenger module and restart Apache.

sudo a2enmod passenger
sudo service apache2 restart

And voila, you now have Apache running with Passenger 3.

Deploying

In a later guide I'll show how to deploy to your server using Capistrano. Capistrano takes some time to setup, but it is very useful because future deployments can then be performed completely automatic with a single command.

In this section we'll deploy a basic rails app by hand.

  1. Move the default page from /var/www to /var/www/default.
  2. Create and deploy a new rails app.
  3. Configure an Apache Virtual host to access your rails app.

1. Moving the default page

We will move de default apache page into a separate directory to prevent unauthorized access to the directories of your Rails app. Failing to do this would make the internals of your application accessible via http://ip.address/railsapp. This could, for example, provide unauthorized access to database passwords.

mkdir /var/www/default
mv /var/www/index.html /var/www/default/index.html

Now edit the default apache vhost to point to this directory.

# /etc/apache2/sites-available/default
...
DocumentRoot /var/www/default
...
<Directory /var/www/default/>
...
  Options FollowSymLinks # Remove Indexes

2. Deploying a new rails app

We'll scaffold a todo rails app at the server to show the deployment of a rails app. To show how bundler installs gems in production, I'll use a separate gemset that is not accessible by Passenger. This simulates the situation in which you develop on a separate computer. We also run bundle install once to generate a Gemfile.lock that bundler will use when deploying. We'll also add therubyracer gem to our Gemfile, because we need a javascript interpreter later.

Note: Bundler is not really setup to both develop and deploy on the same machine. Although the steps below are informative in showing how to deploy an app, I recommend you develop on a separate machine.

rvm use 1.9.3@rails3 --create
gem install rails
cd /var/www
rails new myrailsapp --skip-bundle -d mysql
cd myrailsapp
echo "gem 'therubyracer'" >> Gemfile
bundle install
rails generate scaffold todo name:string finished:boolean
rvm gemset use global

In the new rails app we have to setup the MySQL database.

# /var/www/myrailsapp/config/database.yml
production:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: myrailsapp
  username: myrailsapp
  password: correcthorsebatterystaple

Now change the ownership to the deploy user:

sudo chown -R deploy:deploy /var/www/myrailsapp

We'll run bundler as the deploy user to simulate the behavior we'll expect with capistrano in the next guide. Log in as deploy with ssh before continuing. rake assets:precompile requires a javascript interpreter, good thing you added therubyracer to the Gemfile. Touching tmp/restart.txt restarts passenger.

rvm use 1.9.3 --default
cd /var/www/myrailsapp
bundle install --deployment --without development test
bundle exec rake db:migrate RAILS_ENV=production
bundle exec rake assets:precompile
touch /var/www/myrailsapp/tmp/restart.txt

3. Configure a vhost

The easiest way to check if your rails site is working, is to redirect the default virtual host to your rails app. However, if you have a domain name you could also point the DNS settings to your server and setup a vhost to serve that domain. For simplicity we'll turn off the default vhost and redirect all traffic to our railsapp vhost. Note: the vhost needs to direct to the public folder of your app, everything else is handled by Passenger automagically.

# /etc/apache2/sites-available/myrailsapp
<VirtualHost _default_:80>
  # ServerName www.example.com # Commented out for default
  DocumentRoot /var/www/myrailsapp/public 
  # be sure to point to public!!
  <Directory /var/www/myrailsapp/public/>
    AllowOverride all
    Options -MultiViews
  </Directory>
</VirtualHost>

Now, activate the vhost by running sudo a2ensite myrailsapp, then deactivate the default vhost by running sudo a2dissite default, and then reload the apache configuration using sudo service apache2 reload and send your browser to http://<serverip>/todos. If all went well, you should see your working Rails application.

Continue to part 2 of this guide to learn how to deploy on this setup with capistrano.

Please feel free to notify me iIf anything is not behaving as expected.

Want your own weblog? This on is available on GitHub.

Back to index

Update:

There is an updated version of this guide for ubuntu 12.04 LTS

Original Guide

When I started developing in Rails I found that setting up a Rails development environment is fairly trivial. I had expected no different from setting up a Rails production server. However, having setup a Rails production server over the past few days has thought me that setting up a production environment is a lot more complex than a development environment.

Add to this that although the development process is well documented for new programmers, the production people assume that you are experienced and know what you are doing. Otherwise, why would you be running a production environment in the first place? But, there has to be a first time for everything and with this (and upcoming) guides I hope to make the Rails production life a bit easier.

In this guide I'll show how to setup a Rails production server. In part 2 of this guide I show how to deploy to this server using Capistrano, which will make your deployment process a lot easier. During this setup we'll keep that goal in mind.

The setup

I'm assuming that you already have installed Ubuntu Server with a non-root user (with sudo rights) and that SSH is setup (e.g. like this.) Use sudo, do not use root, because this is a requirement for rvm. You should setup your hosts file for your server. For example, my server has these settings:

# /etc/hosts
127.0.0.1   localhost
12.34.56.78 server.web-l.nl

# /etc/hostname
server.web-l.nl

This should prevent any messages from apache saying that it could not determine a fully qualified domain name (fqdn).

Installing Apache

We'll be installing apache using apt-get. You'll need sudo to do this. While we are installing apache, we might just as well include some packages needed later on.

sudo apt-get install apache2 curl git build-essential zlibc zlib1g-dev zlib1g libcurl4-openssl-dev libssl-dev libopenssl-ruby apache2-prefork-dev libapr1-dev libaprutil1-dev libreadline6 libreadline6-dev

Now redirect your web browser to your servers IP. It should show a welcome page saying: "It works!". Good!

Installing MySQL

Up next is MySQL, our database server. Install MySQL via apt-get, the installer will ask you for a database root password, make sure to pick a strong password.

sudo apt-get install mysql-server

Let's also create a mysql user for your rails app. I'm naming both the database and the user myrailsapp, but you are free to pick any name you feel appropriate. Make sure the replace the password (correcthorsebatterystaple) with something more secure.

create database myrailsapp;
grant all privileges on myrailsapp.* to 'myrailsapp'@'localhost' identified by 'correcthorsebatterystaple'; 
flush privileges;

Installing RVM and Ruby

We'll get to setting up passenger later, but lets first install RVM and a recent ruby. We'll use the curl command provided on the RVM website. Note: This is the first and only time you will use sudo to do something with RVM. After this, a user just needs to be in the RVM group to use rvm. If, for any reason, sudo is needed to do something with rvm, you have to use rvmsudo instead of sudo.

*Note: * The installation script changed location recently, the new command is:

sudo bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer )

This should successfully install RVM in a multi-user install. The next step is to add yourself to the RVM user group. Let's also add a deploy user with RVM access while we're doing this. Add yourself to the deploy group for convenience.
Also set the permissions on the /var/www folder. With these settings every user in the deploy group can modify files in the /var/www folder. Then, log out of your terminal/shell and log back in. (This will update your group permissions)

sudo adduser deploy
sudo adduser deploy rvm
sudo adduser <yourusername> deploy
sudo adduser <yourusername> rvm
sudo chown -R deploy:deploy /var/www
sudo chmod g+w /var/www

Time to install Ruby. You can check the list of available rubies using rvm list known. I have chosen to install ruby 1.9.2-p290, but you could (should?) go for 1.9.3 once a stable release is available. Compilation can take some time, so get a caffeinated beverage of your choice while your server is busy compiling. Then, set the new ruby to be your default ruby and install bundler. (We'll need bundler later)

rvm install 1.9.2 
rvm --default use 1.9.2
gem install bundler

I've had some issues to get openssl and the readline library working with ruby on the beta of Ubuntu Oneiric. One solution is to use rvm pkg install openssl and rvm pkg install readline before installing ruby (you can reinstall ruby using: rvm reinstall 1.9.2. More info can be found at the rvm website.

RVM, Bundler, Rubygems and management

Basically, there are several good ways to manage our gems with respect to Passenger. There also is a wrong way, which is to install all gems in the same rvm gemset on the same server. Good options (in my opinion):

  1. Use a single server for each application. This gives the best separation and redundancy, but it can cost you, especially if you have a lot of low traffic apps.
  2. Using a separate rvm gemset for each application. There are some really good instructions on how to do this in this blog post by Darcy.
  3. Use Bundlers --deployment option to install all gems into the shared/bundle directory of your app. This is what we'll use in this guide, because it does not require any extra setup (unlike option 2).

You ask why I've installed RVM even if I'm going with option 3? Well, this is because rvm makes it easier to install and maintain your desired ruby on Ubuntu. The apt packages for ruby are usually a bit behind.

Installing Passenger

Start by installing passenger. Then install passenger-install-apache2-module using rvmsudo. The module installer is very helpful and will give you directions if anything is missing.

gem install passenger
rvmsudo passenger-install-apache2-module

In the end, the installer will show how to configure the apache configuration files in order to use passenger. Instead of editing the configuration files directly, we'll place 2 files (passenger.load and passenger.conf) in /etc/apache2/mods-available, so that we can use a2enmod and a2dismod to active/deactivate the module. Use sudo to edit these files. The contents below are indicative, use what the passenger installer will return to you. (most likely, the version numbers will change with time)

#/etc/apache2/mods-available/passenger.load
LoadModule passenger_module /usr/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.9/ext/apache2/mod_passenger.so

#/etc/apache2/mods-available/passenger.conf
PassengerRoot /usr/local/rvm/gems/ruby-1.9.2-p290/gems/passenger-3.0.9
PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.2-p290/ruby

Activate the passenger module and restart Apache.

sudo a2enmod passenger
sudo service apache2 restart

And voil, you now have Apache running with Passenger 3.

Deploying

In a later guide I'll show how to deploy to your server using Capistrano. Capistrano takes some time to setup, but it is very useful because future deployments can then be performed completely automatic with a single command.

In this section we'll deploy a basic rails app by hand.

  1. Move the default page from /var/www to /var/www/default.
  2. Create and deploy a new rails app.
  3. Configure an Apache Virtual host to access your rails app.

1. Moving the default page

We will move de default apache page into a separate directory to prevent unauthorized access to the directories of your Rails app. Failing to do this would make the internals of your application accessible via http://ip.address/railsapp. This could, for example, provide unauthorized access to database passwords.

mkdir /var/www/default
mv /var/www/index.html /var/www/default/index.html

Now edit the default apache vhost to point to this directory.

# /etc/apache2/sites-available/default
...
DocumentRoot /var/www/default
...
<Directory /var/www/default/>
...
  Options FollowSymLinks # Remove Indexes

2. Deploying a new rails app

We'll scaffold a todo rails app at the server to show the deployment of a rails app. To show how bundler installs gems in production, I'll use a separate gemset that is not accessible by Passenger. This simulates the situation in which you develop on a separate computer. We also run bundle install once to generate a Gemfile.lock that bundler will use when deploying. We'll also add therubyracer gem to our Gemfile, because we need a javascript interpreter later.

Note: Bundler is not really setup to both develop and deploy on the same machine. Although the steps below are informative in showing how to deploy an app, I recommend you develop on a separate machine.

rvm use 1.9.2-p290@rails31 --create
gem install rails
cd /var/www
rails new myrailsapp --skip-bundle -d mysql
cd myrailsapp
rails generate scaffold todo name:string finished:boolean
echo "gem 'therubyracer'" >> Gemfile
bundle install
rvm gemset use global

In the new rails app we have to setup the MySQL database.

# /var/www/myrailsapp/config/database.yml
production:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: myrailsapp
  username: myrailsapp
  password: correcthorsebatterystaple

We'll run bundler as the deploy user to simulate the behavior we'll expect with capistrano in the next guide. Log in as deploy with ssh before continuing. rake assets:precompile requires a javascript interpreter, good thing you added therubyracer to the Gemfile. Touching tmp/restart.txt restarts passenger.

rvm use 1.9.2-p290 --default
bundle install --deployment --without development test
bundle exec rake db:migrate RAILS_ENV=production
bundle exec rake assets:precompile
touch /var/www/myrailsapp/tmp/restart.txt

3. Configure a vhost

The easiest way to check if your rails site is working, is to redirect the default virtual host to your rails app. However, if you have a domain name you could also point the DNS settings to your server and setup a vhost to serve that domain. For simplicity we'll turn off the default vhost and redirect all traffic to our railsapp vhost. Note: the vhost needs to direct to the public folder of your app, everything else is handled by Passenger automagically.

# /etc/apache2/sites-available/myrailsapp
<VirtualHost _default_:80>
  # ServerName www.example.com # Commented out for default
  DocumentRoot /var/www/myrailsapp/public # be sure to point to public
  <Directory /var/www/myrailsapp/public/>
    AllowOverride all
    Options -MultiViews
  </Directory>
</VirtualHost>

Now, activate the vhost by running sudo a2ensite myrailsapp, then deactivate the default vhost by running sudo a2dissite default, and then reload the apache configuration using sudo service apache2 reload and send your browser to http://<serverip>/todos. If all went well, you should see your working Rails application.

Continue to part 2 of this guide to learn how to deploy on this setup with capistrano.

Back to index