Ubuntu 16.04 Nginx Rails Phusion Server with RVM


Create Swap File

sudo fallocate -l 3G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo swapon --show
free -h
sudo cp /etc/fstab /etc/fstab.bak;
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
sudo apt-get update

Install RVM

sudo apt-get install libgdbm-dev libncurses5-dev automake libtool bison libffi-dev
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm install 2.5.0
rvm use 2.5.0 --default
ruby -v

Install Main Gems

gem install bundler
gem install rails

Install Nginx with Passenger Module

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates
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
sudo apt-get install -y nginx-extras passenger
sudo service nginx start

Place some additional config variables in Nginx. Make sure to uncomment the passenger include config statement.

sudo vim /etc/nginx/nginx.conf

Make sure the following lines are added or uncommented out.

server_names_hash_bucket_size 64;
include /etc/nginx/passenger.conf;
client_max_body_size 110M;
proxy_read_timeout 600s;

Add RVM ruby binary path to passenger lookup.

sudo vim /etc/nginx/passenger.conf

Set the following variables to pertain to what you want to app to have and do.

passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
passenger_ruby /home/ubuntu/.rvm/gems/ruby-2.5.0/wrappers/ruby;
#passenger_ruby /usr/bin/passenger_free_ruby;
passenger_max_pool_size 2;
passenger_min_instances 1;
passenger_pre_start https://myapp.com/;

Restart the web server.

sudo service nginx restart

Install MySQL

sudo apt-get install mysql-server mysql-client libmysqlclient-dev

Add a database and database user.

mysql -u root -p

create database devdb;
create user 'devuser'@'localhost' identified by 'password';
grant all on devdb.* to 'devuser' identified by 'password';

Generate SSH key and Add Your Key

ssh-keygen -t rsa -b 4096 -C "email@gmail.com"
vim .ssh/id_rsa.pub
git clone git@gitlab.com:user/app.git

Add your secrets and environment variables.

cd app/
vim config/secrets.yml
vim .env

Install NodeJS

sudo apt-get install nodejs

Install Your Projects Gems And Setup Production Server

bundle install
rails db:migrate RAILS_ENV=production
rails assets:precompile RAILS_ENV=production
rails db:seed RAILS_ENV=production

Add LetsEncrypt Auto Renewal

Follow my guide here.

NGINX Sites Config

sudo vim /etc/nginx/sites-available/default 
server {
    listen 80;
    listen [::]:80;

    passenger_enabled on;
    rails_env production;
    root  /home/user/app/public;

    server_name www.myapp.com myapp.com; # managed by Certbot
    return 301 https://myapp.com$request_uri;

    location ^~ /.well-known/ {
      root /usr/share/nginx/html;
    }
}

server {
#    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    server_name www.myapp.com;
    ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
    passenger_enabled on;
    rails_env production;
    root  /home/user/app/public;
    return 301 https://myapp.com$request_uri;
}

server {
    listen [::]:443 ssl ipv6only=on; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    server_name myapp.com;
    ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
    ssl_session_cache shared:SSL:10m;
    passenger_enabled on;
    rails_env production;
    root  /home/user/app/public;

    location ^~ /.well-known/ {
      root /usr/share/nginx/html;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;

    location ~*  \.(jpg|jpeg|png|gif|ico|css|js)$ {
      expires 365d;
    }
}

#server {
#       listen 80 default_server;
#       listen [::]:80 default_server;
#
#        passenger_enabled on;
#        rails_env    production;
#        root  /home/user/app/public;
#
#       server_name localhost;
#}

Your options for ssl config should have this.

ssl_session_cache shared:le_nginx_SSL:1m;
ssl_session_timeout 1440m;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";

Check Passenger Installation

sudo /usr/sbin/passenger-status
sudo /usr/sbin/passenger-memory-stats
sudo /usr/bin/passenger-config validate-install
sudo service nginx restart

Make sure gzip is enabled for compression. Feel free to enable additional settings.

sudo vim /etc/nginx/nginx.conf
##
# Gzip Settings
##

gzip on;
gzip_disable "msie6";
gzip_proxied any;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# gzip_vary on;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;

Add Cron Job

0 6 * * * cd /home/user/app && /home/user/.rvm/gems/ruby-2.5.0/wrappers/bundle exec rake task:method RAILS_ENV=production

SSH Guard

Install SSH Guard to prevent brute force attacks through it.

sudo apt-get install sshguard