Deploying a Rails App on Debian Jessie with Capistrano, Nginx, and Puma

Rails is an open source web application framework written in Ruby. It follows the Convention over Configuration philosophy by making assumptions that there is the 'best' way of doing things. This allows you to write less code while accomplishing more without having you go through endless config files.

Nginx is a high performance HTTP server, reverse proxy, and a load balancer known for its focus on concurrency, stability, scalability, and low memory consumption. Like Nginx, Puma is another extremely fast and concurrent web server with a very small memory footprint but built for Ruby web applications.

Capistrano is a remote server automation tool focusing mainly on Ruby web apps. It's used to reliably deploy web apps to any number of remote machines by scripting arbitrary workflows over SSH and automate common tasks such as asset pre-compilation and restarting the Rails server.

In this tutorial we'll install Ruby and Nginx on Debian Jessie and configure Puma and Capistrano in our web app. Nginx will be used to capture client requests and pass them over to the Puma web server running Rails. We'll use Capistrano to automate common deployment tasks, so every time we have to deploy a new version of our Rails app to the server, we can do that with a few simple commands.

Let's configure the repositories

vi /etc/apt/sources.list
# OFFICIAL REPOSITORY
deb http://httpredir.debian.org/debian jessie main contrib non-free
deb-src http://httpredir.debian.org/debian jessie main contrib non-free

# SECURITY UPDATE REPOSITORY
deb http://security.debian.org/ jessie/updates main contrib non-free
deb-src http://security.debian.org/ jessie/updates main contrib non-free

# PROPOSE UPDATE REPOSITORY
deb http://httpredir.debian.org/debian jessie-proposed-updates main contrib non-free
deb-src http://httpredir.debian.org/debian jessie-proposed-updates main contrib non-free

Now we need to update the repositories

apt-get update

Now we need to install the Nginx and the git

apt-get install curl vim git-core nginx -y

Now let's create a user that will be used to deploy the application

useradd -m deploy -s /bin/bash

Now let's set up a password to the new user

passwd deploy

Let's configure the Nginx with some basic configuration to work without any problem

Now let's create a backup of the nginx configuration file.

cp -Rfa /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bkp

Now let's create our configuration as follows.

vim /etc/nginx/nginx.conf
#/etc/nginx/nginx.conf
## User that the server runs as
user www-data;
## how many nginx instances actually run
worker_processes 4;
## Where it stores the process ID of the master process.
pid /run/nginx.pid;

## defines how the daemon incoming requests at the system level
events {
        ## how many connections a sinble worker thread is allowed to process
        worker_connections 768;
        ## keep accepting connections even though the server hasn't finished handeling incoming connections.
        # multi_accept on;
}

## This is where most of your tuning will take place
http {
        ## GLOBAL
        ## Enabling this will increase the speed that nginx can cache, and retreive from cache
        sendfile on;
        ## This option causes nginx to attempt to send it's HTTP response headers in one packet.
        tcp_nopush on;
        ## This disables a buffer that when used with keep-alive connections, can slow things down
        tcp_nodelay on;
        ## Defines the maximum time between keepalive requests from client browsers.
        keepalive_timeout 65;
        ## Defines the maximum size of hash tables. This directly influences cache performance. Higher numbers use more memory, and offer potentially higher performance.
        types_hash_max_size 2048;
        ## Sets the maximum allowed size of the client request body, specified in the “Content-Length” request header field.
        client_max_body_size 20M;
        ## Sets buffer size for reading client request body.
        client_body_buffer_size 128k;
        ## Enables or disables emitting nginx version in error messages and in the “Server” response header field.
        server_tokens off;
        ## Sets the bucket size for the server names hash tables.
        # server_names_hash_bucket_size 64;
        ## Enables or disables the use of the primary server name, specified by the server_name directive, in redirects issued by nginx.
        # server_name_in_redirect off;
        ## Includes mime.types configuration file
        include /etc/nginx/mime.types;
        ## Defines the default MIME type of a response. Mapping of file name extensions to MIME types can be set with the types directive.
        default_type application/octet-stream;

        ## Logging Settings
        access_log /var/log/nginx/access.log combined;
        error_log /var/log/nginx/error.log;

        ## Gzip Settings
        ## Enables or disables gzipping of responses. 
        gzip on;
        ## Disables gzipping of responses for requests with “User-Agent” header fields matching any of the specified regular expressions.
        gzip_disable "msie6";
        ## Enables or disables inserting the “Vary: Accept-Encoding” response header field if the directives gzip, gzip_static, or gunzip are active
        gzip_vary on;
        ## Enables or disables gzipping of responses for proxied requests depending on the request and response.
        gzip_proxied any;
        ## Sets a gzip compression level of a response. Acceptable values are in the range from 1 to 9. 
        gzip_comp_level 6;
        ## Sets the number and size of buffers used to compress a response. By default, the buffer size is equal to one memory page.
        gzip_buffers 16 8k;
        ## Sets the minimum HTTP version of a request required to compress a response.
        gzip_http_version 1.1;
        ##  Enables gzipping of responses for the specified MIME types in addition to “text/html”.
        gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;


        ## Virtual Host Configs
        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

Now we need to disable the default virtual Host

unlink /etc/nginx/sites-enabled/default

Now let's update the repositories

apt-get update

Now let's install the MySQL

apt-get install mysql-server mysql-client libmysqlclient-dev libmysql++-dev libmysqld-dev -y

Note: You will be asked about the root password, so set it up.

Now let's create a database and an user to administrate the database

Let's access the MySQL

mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 37
Server version: 5.5.49-0+deb8u1 (Debian)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

Now we need to create the database

CREATE DATABASE deploy_production;

Now we need to give the permission to the database

GRANT ALL PRIVILEGES ON deploy_production.* TO deploy@'localhost' IDENTIFIED BY 'deploy_password';

Now let's logout from the MySQL

\q

Now let's test the connection

mysql -u deploy -h localhost -p deploy_production
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 42
Server version: 5.5.49-0+deb8u1 (Debian)

Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> \q
Bye

Now let's update the repositories

apt-get update

Now let's install the PostgreSQL

aptitude install postgresql-server-dev-all postgresql postgresql-client -y

Now let's create a user to use the postgresql

Let's change the user to postgres

su - postgres

Now let's call the psql

psql
psql (9.4.8)
Type "help" for help.

Now let's create a new user

CREATE USER deploy PASSWORD 'deploy_password';

Now we need to create the database that will be used by the application.

CREATE DATABASE deploy_production with OWNER deploy;

Now we can logout from the psql

postgres=# \q

Now we can back to the root user

exit

Now we can test the connection

psql -h localhost -U deploy -W -d deploy_production
Password for user deploy: 
psql (9.4.8)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

deploy_production=> \q

Let's import the rvm gpg key

gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3

Now let's install the rvm to manage our Rubies

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

Now if you have more users that will be using the rvm we need to add them into the rvm's group

gpasswd -a deploy rvm

Now we need to import the environment variables

source /etc/profile.d/rvm.sh

Now let's install the dependencies of the rvm

rvm requirements

Now we need to install the ruby

rvm install 2.3.0

Now we need to set as the default

rvm use 2.3.0 --default

Now we need to install the rails and the bundler

gem install rails -V --no-ri --no-rdoc
gem install bundler -V --no-ri --no-rdoc

Three flags were used:

  • -V (Verbose Output): Prints detailed information about Gem installation
  • --no-ri - (Skips Ri Documentation): Doesn't install Ri Docs, saving space and making installation fast
  • --no-rdoc - (Skips RDocs): Doesn't install RDocs, saving space and speeding up installation

Note: You can also install a specific version of Rails according to your requirements by using the -v flag:

gem install rails -v '4.2.0' -V --no-ri --no-rdoc

Now let's get rid of some warnings that will be generate by the rvm about the ruby version

rvm rvmrc warning ignore allGemfiles

Note: This process needs to be configure in both machines in the server that will host the Rails app and in the client machine the developer uses to work with the git that will used to update the sources.

Since we want to set up smooth deployments, we'll be using SSH Keys for authorization. First shake hands with GitHub, Bitbucket, or any other Git Remote where the codebase for your Rails app is hosted:

ssh -T git@github.com
ssh -T git@bitbucket.org

Don't worry if you get a Permission denied (publickey) message. Now, generate a SSH key (a Public/Private Key Pair) for your server:

ssh-keygen -t rsa 
ssh-keygen -t dsa

Add the newly created public key (~/.ssh/id_rsa.pub) to your repository's deployment keys:

If all the steps were completed correctly, you should now be able to clone your git repository (over the SSH Protocol, not HTTP) without entering your password:

git clone git@example.com:username/appname.git

If you need a sample app for testing, you can fork the following test app specifically created for this tutorial: Sample App written in Ruby on Rails on GitHub

The git clone command will create a directory with the same name as your app. For example, a directory named dqs-rails-base will be created.

We are cloning only to check if our deployment keys are working, we don't need to clone or pull our repository every time we push new changes. We'll let Capistrano handle all that for us. You can now delete this cloned directory if you want to.

Open a terminal on your local machine. If you don't have a SSH Key for your local computer, create one for it as well. In your local terminal session:

ssh-keygen -t rsa 

Add your local SSH Key to your Debian Jessie Authorized Keys file (remember to replace the port number with your customized port number):

cat ~/.ssh/id_rsa.pub | ssh -p your_port_num deploy@your_server_ip 'cat >> ~/.ssh/authorized_keys'

Now we need to add private key identities to the authentication agent

ssh-add ~/.ssh/id_dsa
ssh-add ~/.ssh/id_rsa

Now let's clone a project to use as test

git clone https://github.com/douglasqsantos/dqs-rails-base.git

Now let's access the project

cd dqs-rails-base/

On your local machine, create configuration files for Nginx and Capistrano in your Rails application. Start by adding these lines to the Gemfile in the Rails App:

vim Gemfile
ruby '2.3.0'
[...]
group :development do
  # Access an IRB console on exception pages or by using <%= console %> in views
  gem 'web-console', '~> 2.0'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'capistrano',         require: false
  gem 'capistrano-rvm',     require: false
  gem 'capistrano-rails',   require: false
  gem 'capistrano-bundler', require: false
  gem 'capistrano3-puma',   require: false
end

gem 'puma'

Use bundler to install the gems you just specified in your Gemfile. Enter the following command to bundle your Rails app:

bundle install

Now we need to create the production key to run the application

rake secret
46d1feba0000226fdf16e9bd94739c7a09fd24401ff8ba33945161fe613a0211459a9167b6267eb796b66167ddf187f4a40a44460a46a9adfeecd875161ea5ac

Now we need to pass it into config/secrets.yml

vim config/secrets.yml
[...]
production:
  secret_key_base: b98f316c6a54b6c76ab01859945b78aef9ec0e051e55a30b530957441420057ab3e42fcff7fa308a488a689ee61f10e10ccc07040b190d839f96c7803f50e078

After bundling, run the following command to configure Capistrano:

cap install

This will create:

  • Capfile in the root directory of your Rails app
  • deploy.rb file in the config directory
  • deploy directory in the config directory

Replace the contents of your Capfile with the following:

vim Capfile
# Load DSL and Setup Up Stages
require 'capistrano/setup'
require 'capistrano/deploy'

require 'capistrano/rails'
require 'capistrano/bundler'
require 'capistrano/rvm'
require 'capistrano/puma'

# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

This Capfile loads some pre-defined tasks in to your Capistrano configuration files to make your deployments hassle-free, such as automatically:

  • Selecting the correct Ruby
  • Pre-compiling Assets
  • Cloning your Git repository to the correct location
  • Installing new dependencies when your Gemfile has changed

Replace the contents of config/deploy.rb with the following, updating fields marked as change_me with your app and Debian Jessie parameters:

vim config/deploy.rb
# Change these
server 'change_me', port: change_me, roles: [:web, :app, :db], primary: true

set :repo_url,        'git@github.com:change_me/dqs-rails-base.git'
set :application,     'dqs-rails-base'
set :user,            'deploy'
set :puma_threads,    [4, 16]
set :puma_workers,    0

# Don't change these unless you know what you're doing
set :pty,             true
set :use_sudo,        false
set :stage,           :production
set :deploy_via,      :remote_cache
set :deploy_to,       "/home/#{fetch(:user)}/apps/#{fetch(:application)}"
set :puma_bind,       "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
set :puma_state,      "#{shared_path}/tmp/pids/puma.state"
set :puma_pid,        "#{shared_path}/tmp/pids/puma.pid"
set :puma_access_log, "#{release_path}/log/puma.error.log"
set :puma_error_log,  "#{release_path}/log/puma.access.log"
set :ssh_options,     { forward_agent: true, port: 22022,  user: fetch(:user), keys: %w(~/.ssh/id_rsa.pub) }
set :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true  # Change to false when not using ActiveRecord

## Defaults:
# set :scm,           :git
# set :branch,        :master
# set :format,        :pretty
# set :log_level,     :debug
# set :keep_releases, 5

## Linked Files & Directories (Default None):
# set :linked_files, %w{config/database.yml}
# set :linked_dirs,  %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}

namespace :puma do
  desc 'Create Directories for Puma Pids and Socket'
  task :make_dirs do
    on roles(:app) do
      execute "mkdir #{shared_path}/tmp/sockets -p"
      execute "mkdir #{shared_path}/tmp/pids -p"
    end
  end

  before :start, :make_dirs
end

namespace :deploy do
  desc "Make sure local git is in sync with remote."
  task :check_revision do
    on roles(:app) do
      unless `git rev-parse HEAD` == `git rev-parse origin/master`
        puts "WARNING: HEAD is not the same as origin/master"
        puts "Run `git push` to sync changes."
        exit
      end
    end
  end

  desc 'Initial Deploy'
  task :initial do
    on roles(:app) do
      before 'deploy:restart', 'puma:start'
      invoke 'deploy'
    end
  end

  desc 'Restart application'
  task :restart do
    on roles(:app), in: :sequence, wait: 5 do
      invoke 'puma:restart'
    end
  end

  desc 'Seeding Database'
  task :seed do
   puts "\n=== Seeding Database ===\n"
   on primary :db do
    within current_path do
      with rails_env: fetch(:stage) do
        execute :rake, 'db:seed'
      end
    end
   end
  end

  desc 'Migrating Down Database'
  task :migrate_down do
   puts "\n=== Migrating Up Database ===\n"
   on primary :db do
    within current_path do
      with rails_env: fetch(:stage) do
        execute :rake, 'db:migrate VERSION=0 --trace'
      end
    end
   end
  end

  desc 'Migrating Up Database'
  task :migrate_up do
   puts "\n=== Migrating Up Database ===\n"
   on primary :db do
    within current_path do
      with rails_env: fetch(:stage) do
        execute :rake, 'db:migrate --trace'
      end
    end
   end
  end

  before :starting,     :check_revision
  after  :finishing,    :compile_assets
  after  :finishing,    :cleanup
  after  :finishing,    :restart
end

# ps aux | grep puma    # Get puma pid
# kill -s SIGUSR2 pid   # Restart puma
# kill -s SIGTERM pid   # Stop puma

This deploy.rb file contains some sane defaults that work out-of-the-box to help you manage your app releases and automatically perform some tasks when you make a deployment:

  • Uses production as the default environment for your Rails app
  • Automatically manages multiple releases of your app
  • Uses optimized SSH options
  • Checks if your git remotes are up to date
  • Manages your app's logs
  • Preloads the app in memory when managing Puma workers
  • Starts (or restarts) the Puma server after finishing a deployment
  • Opens a socket to the Puma server at a specific location in your release

You can change all options depending on your requirements. Now, Nginx needs to be configured. Create config/nginx.conf in your Rails project directory, and add the following to it (again, replacing with your parameters):

vim config/nginx.conf
#config/nginx.conf
upstream puma {
  server unix:///home/deploy/apps/dqs-rails-base/shared/tmp/sockets/appname-puma.sock;
}

# Default server configuration
server {

  ## Sets the address and port for IP, or the path for a UNIX-domain socket on which the server will accept requests.
  listen 80;
  
  ## Sets names of a virtual server
  server_name rails.douglasqsantos.com.br;

  ## Enables or disables emitting nginx version in error messages and in the “Server” response header field.
  server_tokens off;
  
  ## Sets the root directory for requests
  root /home/deploy/apps/dqs-rails-base/current/public;
  
  ## Logging
  access_log /home/deploy/apps/dqs-rails-base/current/log/nginx.access.log;
  error_log /home/deploy/apps/dqs-rails-base/current/log/nginx.error.log info;

  ## Sets configuration for assets
  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  ## Sets the configuration for all files and the puma web server
  try_files $uri/index.html $uri @puma;
  location @puma {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    proxy_pass http://puma;
  }

  ## Default error pages
  error_page 500 502 503 504 /500.html;
  
  ## Sets configuration for max body size as 10MB
  client_max_body_size 10M;
  
  ## Sets How long an idle keepalive connection remains open.
  keepalive_timeout 10;
}

Like the previous file, this nginx.conf contains defaults that work out-of-the-box with the configurations in your deploy.rb file. This listens for traffic on port 80 and passes on the request to your Puma socket, writes nginx logs to the 'current' release of your app, compresses all assets and caches them in the browser with maximum expiry, serves the HTML pages in the public folder as static files, and sets default maximum Client Body Size and Request Timeout values.

If you are using your own Rails app, commit the changes you just made, and push them to remote from your Local Machine:

git add -A
git commit -m "Set up Puma, Nginx & Capistrano"
git push origin master

Note: If this is the first time using GitHub from this system, you might have to issue the following commands with your GitHub username and email address:

git config --global user.name 'Your Name'
git config --global user.email you@example.com

Again, from your local machine, make your first deployment:

cap production deploy:initial

This will push your Rails app to the Debian Jessie, install all required gems for your app, and start the Puma web server. This may take anywhere between 5-15 minutes depending on the number of Gems your app uses. You will see debug messages as this process occurs.

If you need to run a seed of the database for example we can use the following command

cap production deploy:seed

If you need to run a migration to a version 0

cap production deploy:migrate_down

If you need to run a migration

cap production deploy:migrate_up

Another good feature that we need to run sometimes is restart the puma server

cap production deploy:restart

If everything goes smoothly, we're now ready to connect your Puma web server to the Nginx reverse proxy.

On the Debian Jessie, Symlink the nginx.conf to the sites-enabled directory:

ln -nfs "/home/deploy/apps/dqs-rails-base/current/config/nginx.conf" "/etc/nginx/sites-enabled/dqs-rails-base"

Restart the Nginx service:

systemctl restart nginx

You should now be able to point your web browser to your server IP and see your Rails app in action!

Whenever you make changes to your app and want to deploy a new release to the server, commit the changes, push to your git remote like usual, and run the deploy command:

git add -A
git commit -m "Deploy Message"
git push origin master
cap production deploy

Note: If you make changes to your config/nginx.conf file, you'll have to reload or restart your Nginx service on the server after deploying your app:

service nginx restart

The idea here is when we restart the server machine for some reason we will get the puma server working in the boot time with the system and everything will work properly.

Let's create the service

vim /etc/systemd/system/puma.service
[Unit]
Description=Puma Control
After=network.target auditd.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/puma/puma-start
ExecStop=/etc/puma/puma-stop

[Install]
WantedBy=multi-user.target

Now we need to create the directory that will store the scripts

mkdir /etc/puma

Now let's create the script that will start the service

vim /etc/puma/puma-start
#!/bin/bash

cd /home/deploy/apps/dqs-rails-base/current && ( export RACK_ENV="production" ; /usr/local/rvm/bin/rvm default do bundle exec puma -C /home/deploy/apps/dqs-rails-base/shared/puma.rb --daemon )

Now let's give the permission to the script

chmod +x /etc/puma/puma-start

Now let's create the script that will stop the service

vim /etc/puma/puma-stop
#!/bin/bash

cd /home/deploy/apps/dqs-rails-base/current && ( export RACK_ENV="production" ; /usr/local/rvm/bin/rvm default do bundle exec pumactl -S /home/deploy/apps/dqs-rails-base/shared/tmp/pids/puma.state stop )

Now let's give the permission to the script

chmod +x /etc/puma/puma-stop

Now let's enable the service

systemctl enable puma

Now we can handle the puma service with the systemd

systemctl start puma
systemctl stop puma
systemctl restart puma
systemctl status puma

Now we can restart the server and check if everything is working properly.

Let's access the root directory of the application on the local machine

cd ~/dev/dqs-rails-base

Let's create the directory to store the certificates

mkdir config/ssl

Let's access the directory

cd config/ssl

Let's create the certificate key

openssl genrsa -des3 -out server.key 1024
Generating RSA private key, 1024 bit long modulus
..............................++++++
.++++++
e is 65537 (0x10001)
Enter pass phrase for server.key: #PASSWORD
Verifying - Enter pass phrase for server.key: #PASSWORD

Now we need to create the certificate sign request

openssl req -new -key server.key -out server.csr
Enter pass phrase for server.key: #PASSWORD
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:BR
State or Province Name (full name) [Some-State]:Parana
Locality Name (eg, city) []:Curitiba
Organization Name (eg, company) [Internet Widgits Pty Ltd]:DQS
Organizational Unit Name (eg, section) []:IT
Common Name (e.g. server FQDN or YOUR name) []:rails.douglasqsantos.com.br
Email Address []:douglas.q.santos@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:DQS

Now we need to change the certificate permissions

chmod 0400 server.*
cp server.key server.key.orig

Now we need to auto sign the certificate

openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt
Signature ok
subject=/C=BR/ST=Parana/L=Curitiba/O=DQS/OU=IT/CN=rails.douglasqsantos.com.br/emailAddress=douglas.q.santos@gmail.com
Getting Private key
Enter pass phrase for server.key: #PASSWORD

Now let's remove the password from our certificate, otherwise we need to put the password every nginx start.

openssl rsa -in server.key.orig -out server.key
Enter pass phrase for server.key.orig: #PASSWORD
writing RSA key

Let's change the certificates permissions.

chmod 0400 ~/dev/dqs-rails-base/config/ssl/* 

Now let's create a backup of the nginx configuration

cp -Rfa ~/dev/dqs-rails-base/config/nginx.conf{,.bkp}

Now let's configure the nginx to use the ssl

vim /var/www/html/dqs-rails-base/config/nginx.conf
# Puma configuration
upstream puma {
  server unix:///home/deploy/apps/mygym/shared/tmp/sockets/mygym-puma.sock;
}

# Default server configuration
server {
  ## Sets the address and port for IP, or the path for a UNIX-domain socket on which the server will accept requests.
  listen 443;
  
  ## Sets names of a virtual server
  server_name rails.douglasqsantos.com.br;
  
  ## Enables or disables emitting nginx version in error messages and in the “Server” response header field.
  server_tokens off;
  
  ## Enables the HTTPS protocol for the given virtual server.
  ssl on;
  
  ## Specifies a file with the certificate in the PEM format for the given virtual server.
  # if you are using an valid certificate need to do the following process to use it.
  # cat server_name.crt CertCA.crt >> server.crt
  ssl_certificate /home/deploy/apps/dqs-rails-base/current/config/ssl/server.crt;
  
  ## Specifies a file with the secret key in the PEM format for the given virtual server.
  ssl_certificate_key /home/deploy/apps/dqs-rails-base/current/config/ssl/server.key;

  ### Enable the timout of the ssl session to 5 minutes
  ssl_session_timeout  5m;
  
  ## Specifies the enabled protocols
  ssl_protocols  SSLv2 SSLv3 TLSv1;
  
  ## Specifies the enabled ciphers
  ssl_ciphers  HIGH:!aNULL:!MD5;
  
  ## Specifies the enabled ciphers
  ssl_prefer_server_ciphers   on;

  ## Sets the root directory for requests
  root /home/deploy/apps/dqs-rails-base/current/public;
  
  ## Logging
  access_log /home/deploy/apps/dqs-rails-base/current/log/nginx.access.log;
  error_log /home/deploy/apps/dqs-rails-base/current/log/nginx.error.log info;

  ## Sets configuration for assets
  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  ## Sets the configuration for all files and the puma web server
  try_files $uri/index.html $uri @puma;
  location @puma {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto https;
    proxy_redirect off;
    proxy_pass http://puma;
  }

  ## Default error pages
  error_page 500 502 503 504 /500.html;
  
  ## Sets configuration for max body size as 10MB
  client_max_body_size 10M;
  
  ## How long an idle keepalive connection remains open.
  keepalive_timeout 10;
}

Now After change the file needs to commit and push to the repo, after that we need to deploy to the server

cap production deploy

Now we need to restart the Nginx

systemctl restart nginx

Now we can access https://ip_server or https://rails.douglasqsantos.com.br

Okay, so by now you would be running a Rails app on your Debian Jessie with Puma as your Web Server as well as Nginx and Capistrano configured with basic settings.

You should now take a look at other docs that can help you optimize your configurations to get the maximum out of your Rails application:

References