/Rail-AWS-Config

Step by Step guide to deploy a Rail app to AWS EC2 using Passenger

Step By Step Guide to Deploy a Rails App to AWS EC2

This documentation takes you through the process of configuration required to host a Rails application on a EC2 instance. The tutorial uses Nginx + Passenger on the server. The sample application used in this guide can be found here

A) Creating a new AWS EC2 instance

  1. Open AWS Console

  2. After you signin into your developer account, you should see a list of AWS services. From the the services find Compute and select EC2 from there. aws_step_1

  3. Select Launch Instance from the EC2 dashboard: aws_step_2

  4. Choose Ubuntu Server 16.04 LTS from the list of AMI (Amazon Machine Image): aws_step_3

  5. Choose an instance type. For purposes of this demo - choose t2.micro: aws_step_4

  6. Keep on clicking Next: .... from Step 3 to Step 5 in the dashboard. On "Step 6: Configure A Security Group", there:

    • Click on Add Rule button and add a new rule for HTTP by selecting HTTP from the dropdown.
    • In the Source column click on the dropdown and choose Anywhere so that you can ssh into your instance from any local machine.

At the end of this step, your console should look something like this: aws_step_5

  1. Click on Review and Launch and then click on Launch on next screen. You will prompted to create a new publick key which will be used to SSH into your instance. After your create a new public key, hit Download Key Pair. aws_step_6

  2. At this point, you have succesfully launched an AWS EC2 instance. Click on View Instances to see your newly deployed instance. aws_step_7 aws_step_8

B) SSH into your instance

Open a terminal of your choice and type the following command. Replace the xx.xx.xx.xx with your Public IP from the dasboard. The default usernames provided by EC2 are admin, ubuntu and ec2-user:

$~ >> ssh -i ~/path/to/your-pem-file.pem ubuntu@xx.xx.xx.xx 

If you get an error saying "permission too open", then change the permissions on the file by typing:

$~ >> chmod 400 your-pem-file.pem

If everything went well you should be able to ssh int your server successfully. ssh_1

C) Prepare the server:

Install essential packages:

  1. Update and Upgrade the package manager apt:
$~ >> sudo apt update && sudo apt upgrade
  1. Install build curl, gpg and build-essentials packages. Note: Since Ubuntu 16.04 LTS's realease, you can now use apt instead of apt-get! You are free to choose either:
$~ >> sudo apt install -y curl gnupg build-essential

Install Ruby and RVM:

  1. Install RVM (Ruby Version Manager):
$~ >> sudo gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
$~ >> curl -sSL https://get.rvm.io | sudo bash -s stable
$~ >> sudo usermod -a -G rvm `whoami`
  • Confirm rvm was installed by typing rvm --version. If you don't see the following output, logout of your instance and login again and then try the command: shell_rvm_1
  1. Run the following commands, to make sure the environment variable rvmsudo_secure_path is set to 1. On macOS it is found that, the following command is needed to set the correct environment variable to avtivate rvm:
$~ >> if sudo grep -q secure_path /etc/sudoers; then sudo sh -c "echo export rvmsudo_secure_path=1 >> /etc/profile.d/rvm_secure_path.sh" && echo Environment variable installed; fi

shell_rvm_2

  1. Install Ruby:
$~ >> rvm install ruby
$~ >> rvm --default use ruby

The above command installs the latest version of the ruby. To install a specific version you can do:

$~ >> rvm install ruby-2.3.0
$~ >> rvm --default use ruby-2.3.0

If Ruby was installed correctly, you can check its version by running the following command: ruby -v and playing around with the Interactive Ruby Console (irb):

shell_irb

Install bundler

Bundler is a popular tool for managing application gem dependencies. To install run the following command:

$~ >> gem install bundler --no-rdoc --no-ri

Install NodeJS

Rails uses NodeJS to precompile the asset pipeline. To install Node:

$~ >> sudo apt-get install -y nodejs &&
      sudo ln -sf /usr/bin/nodejs /usr/local/bin/node

shell_node_install

D) Install Passenger Web Server

Passenger is an open source web application server for Ruby. It handles HTTP requests, manages processes and resources, and enables administration, monitoring and problem diagnosis. Passenger is very easy to use, makes deploying in production much easier and is scalable.

To install Passenger on your EC2 instance follow the commands below:

  1. Install Passenger's PGP Key and add HTTPS support for APT:
$~ >> sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7 &&
sudo apt-get install -y apt-transport-https ca-certificates
  1. Add Passenger's APT repository:
$~ >> sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main > /etc/apt/sources.list.d/passenger.list' && sudo apt-get update
  1. Install Passenger + Nginx
$~ >> sudo apt-get install -y nginx-extras passenger

shell_passenger_nginx

  1. Now we will enable Passenger Nginx module:
  • sudo nano /etc/nginx/nginx.conf
  • Uncomment the line in the http block by removing the # symbol, that says: # include /etc/nginx/passenger.conf;
  • To save the file press: Ctrl+O => Press the enter key => Ctrl+X
  • If you do not see a http block, add it yourself:
  http {
    include /etc/nginx/passenger.conf
  }
  • shell_passenger_conf
  • Finally restart the Nginx service:
$~ >> sudo service nginx restart
  1. If everything went well and Nginx with Passenger was installed correctly, you should see the following page when you visit your EC2 instance's public IP: nginx

  2. Validate if Passenger was installed or not correctly:

validate_passenger

E) Deploy App

Add new user

  1. Add new user:
$~ >> sudo adduser abhi

sudo_add_user

  1. Give new user sudo permissions:
$~ >> sudo visudo

A editor will open up, find root ALL=(ALL:ALL) ALL under User privilege section and add the following line:

root    ALL=(ALL:ALL) ALL
abhi    ALL=(ALL:ALL) ALL

adduser

To save the file press: Ctrl + O => Hit enter to save the file name => Ctrl+Y

  1. Add the new user to sudo group: sudo_group

Install the public key for new user

  1. Issue the following command to get the public key. Copy the output of the command, we will use this in th next step.
$~ >> cat ~/.ssh/authorized_keys
  1. Create a .ssh directory and make a file authorized_keys there and set correct permissions on them:

ssh_dir

  1. Edit the ~/.ssh/authorized_keys file by:
$~ >> nano ~/.ssh/authorized_keys
  1. Paste the public key you copied on step 1 here and save the file and exit.

  2. Enter exit twice to exit out of the server.

  3. If everything worked correctly, you should now be able to log in to your server by the new user you created! ssh_new_user

Note: Creating a new user is very important, as the root user doesn't throw any confirmation messages when you run commands - which can be dangerous. The new user has to always use sudo which asks for password and saves us from any wrong command being issued.

Install Git on server

I have already deployed my project on Github, since it is easier to transfer files from a git server than using FTP, but you may suit yourself.

Note: The commands henceforth are written from the new user account and not the root user, unless otherwise mentioned

  1. Install Git:
$~ >> sudo apt install -y git
  1. Make a new directory under /var/www and clone the project there:
$~ >> sudo mkdir /var/www/photosite-webapp
$~ >> sudo chown your-new-user-name: /var/www/photosite-webapp
  1. Clone your project from github into /var/www/photosite-webapp with correct user permissions:
$~ >> sudo -u your-new-user-name -H git clone https://your-github-project-url

If everything worked correctly, you should now have your project's code on your server:

git_setup

Install project dependencies

  1. If you're already not inside your project directory, then go into your project directory:
$~ >> cd /var/www/photosite-webapp/Project-Name-On-Github
$~ >> ls
  1. Make sure your Gemfile has the following gem sqlite3 outside of all the groups and then run the following commands (libpq-dev is used to install native extensions for pg gem) :
$~ >> sudo apt-get install libpq-dev
$~ >> bundle install --deployment --without development test

Configure config/database.yml and config/secrets.yml

$~ >> sudo nano config/database.yml

and make sure it looks like: database

  1. Rails also needs a unique secret key with which to encrypt its sessions. Starting from Rails 4, this secret key is stored in config/secrets.yml. But first, we need to generate a secret key. Run:
$~ >> bundle exec rake secret

This command will output a secret key, copy that and put it inside config/secrets.yml's production section: prod_secret

  1. Change the security permissions on DB and secrets.yml file:
$~ >> chmod 700 config db
$~ >> chmod 600 config/database.yml config/secrets.yml
  1. Run the following command to precompile assets and run migrations:
$~ >> bundle exec rake assets:precompile db:migrate RAILS_ENV=production

Output: assets_and_migrations

Final Steps

  1. Determin the ruby command that Passenger should use:
$~ >> passenger-config about ruby-command

Please copy the path given for the Command variable, for me it looks like this:

Command: /usr/local/rvm/gems/ruby-2.4.0/wrappers/ruby

  1. Create a new file photosite-webapp.conf under /etc/nginx/sites-enabled/:
$~ >> sudo nano /etc/nginx/sites-enabled/photosit-webapp.conf
  1. Add the following server block in the file: server_block

Please note that, on the line passenger_ruby - put whatever you copied on Step 1 of this section and on the line server_name put your EC2 instance's public IP or if you have a DNS, put it's name there.

  1. Finally, restart the server:
$~ >> sudo service nginx restart

If everything worked correctly, visit your Public IP of EC2 (for me): 34.208.175.125 - and you should see your app running!

Unfortunately if you faced errors, type the following command to see the error logs:

$~ >> sudo tail -n 20 /var/log/nginx/error.log

Screenshots from the live app:

live_app_1 live_app_3 live_app_2