Recap

This post is a continuation from here. Previously we have installed Ruby, Rails, Nginx, Passenger and PostgreSQL. On this post we will install git, configure nginx config file to deploy rails app and git push the rails app from your development machine to the VPS. The step number will continue from the previous one, which is eleven.

Step Eleven - Install git and creating a repository

Install git on the VPS using this command :
sudo apt-get install git

After installing Git, we will now create a bare git repository named awesomeapp.git and a folder to store the source code awesomeapp, let's put the repository on your user folder for this tutorial ( you can of course change to other location ).
cd ~
mkdir awesomeapp
mkdir repo && cd repo
mkdir awesomeapp.git && cd awesomeapp.git
git init --bare

--bare indicates that the repository will have no working files / source code, just the version control.

Type pwd to get your present working directory, you will need this later. My output looks like this :
pwd

Next, we will dive into the git hooks to add a work tree directory (where your source code will be stored), more info about git hooks here. We will use the post-receive hook as it is executed after a git push has finished receiving.

Inside awesomeapp.git directory, if you type ls, you can see the list of files and folders like this :
git inner mysteries

Go to the hooks folder
cd hooks
Create a file named 'post-receive' :
cat > post-receive


When you execute the command above, you will see a blank line indicating that everything you type will be saved to this file. Let's proceed to type :

#!/bin/sh
git --work-tree=/home/soulchild/awesomeapp --git-dir=/home/soulchild/repo/awesomeapp.git checkout -f

Replace the work-tree directory with your desired location, this is the place where the source code will be saved.
Replace the git-dir directory with the bare git repository you created just now.

After finish typing, press Ctrl + D to save. We need to allow permissions for the file to be executable :
chmod +x post-receive

Now the source code will be saved in the work-tree every time a git push is received by the awesomeapp.git repository.

{% comment %}

Optional but recommended step - Set up git hooks

Git hooks are trigger points which you can define commands that will be executed whenever a specific event is triggered. ( eg : restart Nginx server after a git push is received )
Continuing from previous step, we will edit the post-receive hook for the awesomeapp git repository.
cd .git

We are inside the inner working of git now, if you type ls to list the directories, you will see output similar to this :
Inner mysteries of how git works...or not

Now lets go to hooks folder

{% endcomment %}

Step Twelve - Push the Rails app from your local machine to VPS

Now on your local machine ( your laptop/pc ), locate to your existing Rails application or create one if you don't have one :
rails new awesomeapp -d postgresql

If you are creating a new Rails application, make sure to point the root route to a controller action first.

Open /config/database.yml of the rails app, scroll to the bottom and edit the production database username to look like this :

We will also generate a secret key for the production secret key base.
cd awesomeapp
rake secret

Keep this a secret! Shhh
Copy down the string generated, we will need it later for setting environment variables.

Remember the present working directory from the previous step? We will add it to the git remote of the Rails app. If your local rails application has not initialize a git repository yet,
#inside awesomeapp rails root folder
#cd awesomeapp
git init

Then add the remote :

git remote add live ssh://user@YOUR_SERVER_IP_OR_DOMAIN_NAME/repo_location

live is the name of the remote, you can change this to whatever name you like.
user is the username you used to login to the ssh, change this to your ssh username.
YOUR_SERVER_IP_OR_DOMAIN_NAME is pretty much self-explanatory.
repo_location is the location of the repository in the VPS, replace this with the bare git repository mentioned in the previous step (eg: 'home/soulchild/repo/awesomeapp.git').

And now you are ready to push to the remote VPS, replace live with the remote name you have chosen just now.
git add -A
git commit -m "Awesome app first commit"
git push live master

Sweet! Next, let's edit the Nginx configuration to serve the Rails app and include environment variables as well.

Step Thirteen - Edit Nginx Configuration file and deploy

Connect to your VPS using ssh
ssh demo@SERVER_IP_ADDRESS
Edit the Nginx configuration file using nano
sudo nano /opt/nginx/conf/nginx.conf

In the server block inside http block, edit it to look like this :

server {
        listen       80;
        server_name  localhost;
        root /path/to/rails_app_root/public; # <-- be sure to point to 'public'
        passenger_app_root /path/to/rails_app_root;
        passenger_enabled on;
        rack_env production;
        passenger_env_var SECRET_KEY_BASE d6532f57f0e1f5150f38ef413fd85c2c9081195431177950d6e44248822ab3d05bb2619871c29a8367d95ff04abaaba84bfc4433f37d089c9437c3e2e1efadc6;
        passenger_env_var AWESOMEAPP_DATABASE_USERNAME demo;
        passenger_env_var AWESOMEAPP_DATABASE_PASSWORD correcthorsebatterystaple;
}
  1. Replace the /path/to/rails_app_root/ with the work tree you defined in previous step.
  2. rack_env is the environment of the rails app, we set it to production.
  3. passenger_env_var is used to define environment variables, you can define multiple environment variables in the config file. Replace the value of environment variables above with yours.

Press Ctrl + X to finish editing, type 'Y' and press Enter when prompted to save.


Your nginx.conf will look similar to this :

Now we will move to the Rails application root directory (the work tree),
cd ~/awesomeapp

Run bundle install
bundle install

Precompile assets, remember to set the RAILS_ENV to production
rake assets:precompile RAILS_ENV=production

Restart the Nginx server first so that the environment variables will be loaded :
sudo service nginx restart

Run rake:db create to create database
rake db:create RAILS_ENV=production

Run rake:db migrate to migrate database
rake db:migrate RAILS_ENV=production

And finally restart the Passenger app to update the changes
passenger-config restart-app /path/to/rails_app_root

Step Fourteen - Cheers!

Now navigate to your VPS IP /domain name in the browser, you should see your Rails app running. 🍻
Bye Heroku

Next time whenever you git push a new update to remote, remember to login to the VPS and run these commands :

  1. bundle install
  2. rake assets:precompile RAILS_ENV=production
  3. rake db:migrate RAILS_ENV=production
  4. passenger-config restart-app /path/to/rails_app_root

Passenger app server must be restarted to reflect the changes after every git push.

Wait.... why do we need to repetitively execute these commands after git push, there must be a better way to automate this right? Yes there is! we can add these command to run in the post-receive hook, remember?

Step Fifteen - Adding deployment task to post-receive hooks

Lets go to the repository of the rails app again and edit the post-receive hook :
cd ~/repo/awesomeapp.git
cd hooks
nano post-receive

We will edit it to look like this :

Replace the work tree and git dir accordingly, press Ctrl + X to end editing.

Edited 19 July 2016 : I found that restarting the Passenger app is sufficient without restarting Nginx, no sudo permission is required for restarting passenger app.


Notice that there is a sudo command which is the sudo service nginx restart, executing a sudo command requires a password input hence the restart command will not run in this post-receive hook. We have to manually set that no password is required for this command (nginx) for the user (the sudo user you created).


Let's edit the user file in sudoers.d directory, replace demo with your username :

sudo visudo -f /etc/sudoers.d/90-demo


Add this line to the end of the file , replace demo with your username :

demo ALL=(ALL) NOPASSWD:/etc/init.d/nginx
Press Ctrl + X to finish editing, type 'Y' and press Enter to save.

Now you should be good to go. In your local machine development repository, make a commit and push it the remote, you will see an output like this :
Git push looks more magical now

And after receiving push, the server will run those deployment commands automatically and the web pages has changed :
Look! No manual restart!

Now every time you push master branch to remote, the remote will deploy automatically. Sounds like Heroku? 😜

Congratulation for making this far! 🎉 You have learnt how to deploy Rails app on a bare VPS using git flow.