Deploying Rails to Production
I recently launched Quiniela de Nacimiento with my client MEXICO SIM CARD. It was my first production deployment of a Rails application. When I researched and planned the deployment, most of the tutorials I could find were about deploying to Heroku or using automation tool such as Capistrano. My objective was to get the application up and running on a server hosted in Amazon EC2. I also did not want to rely on automation deployment at this time. I took notes along the way and summarized the technical steps I took bellow.
The Production Database
I relied on SQLite during development but wanted something more robust for production. I chose PostgreSQL and decided to use the Amazon RDS to host the database. I followed the instructions detailed here to create the database.
The Application Server
When creating a server in EC2, there are several Linux image choices. I opted for Ubuntu Server 16.04 LTS AMI. LTS releases are supported for five years by Canonical and Ubuntu Server is widely used. In my experience, it's easier to find answers on the web when facing a problem with Ubuntu than if you are using another Linux distribution. This guide list all the steps required to boot a new server in EC2.
To avoid putting sensitive information directly in the source code, I opted for using environment variables which are supported by Rails out of the box. To do so, I edited
/etc/environment on my new Ubuntu server to include the credentials and other sensitive information used by the application.
Before installing anything new, I made sure that my server was up to date by updating the package repositories and upgrading the installed software to the latest version:
sudo apt-get update
sudo apt-get upgrade
I then proceeded to install the required packages to:
- build ruby
- connect to SQLite (optional) and PostgreSQL
- serve static content with nginx
sudo apt-get install build-essential nginx zlib1g-dev \
libssl-dev libreadline-dev libyaml-dev libsqlite3-dev \
sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev \
libffi-dev nodejs libpq-dev
To build and install Ruby 2.4.1 on the Ubuntu server, I executed the following commands:
tar xf ruby-2.4.1.tar.gz
sudo make install
At this point,
ruby -v was outputting the correct Ruby version (2.4.1).
The project was developed using Rails 5.0.1. With Ruby now installed, I executed the following command to install Rails as well:
sudo gem install rails -v 5.0.1
At this point,
rails -v was outputting the correct Rails version (5.0.1).
Deploy Application Code
With the software requirements in place, I was ready to install the application code on the server. I cloned the repository and proceeded to:
- install required gems
- prepare the production database
- prepare static assets
To achieve this, I ran the following commands from the folder hosting the Rails application files:
RAILS_ENV="production" bin/rails db:setup
RAILS_ENV=production bin/rails assets:precompile
Since this is production, I also configured the application secret key by entering the following command and copying the result inside /etc/profile:
RAILS_ENV=production rails secret
Still in the application folder, I created two new folders that will be used by the Puma server which will execute the Ruby code:
mkdir tmp/sockets tmp/pids
Ubuntu now uses systemd as its service manager. To interract with Puma and to start it at boot, I created a new systemd service. To achieve this, created a file named
/lib/systemd/system/ with content similar to this. As you can see, the Puma service uses environment variables to pass sensitive information to the application.
To enable and start this new service, I used this two commands:
sudo systemctl enable rails-puma
sudo systemctl start rails-puma
When running in production, Puma is not serving the static content. I selected NGINX for this task. A site configuration similar to this allows to serve static assets through NGINX while proxying application requests to Puma. The details regarding the https settings were configured through Certbot. With this new site configuration in place, the last step was to restart the NGINX service:
sudo systemctl restart nginx
At this point, the website became available when reaching Quiniela de Nacimiento.
I plan to automate some of these steps or maybe investigate Capistrano for my next deployment. However, I feel that my decision to go fully manual for this deployment gave me a better understanding of how a Rails application work outside of my development environment.
Also published on Medium