Skip to content

Instantly share code, notes, and snippets.

@PixieStudio
Last active September 28, 2021 20:39
Show Gist options
  • Select an option

  • Save PixieStudio/336ec22e76e67508bb9be98d6cdfba9b to your computer and use it in GitHub Desktop.

Select an option

Save PixieStudio/336ec22e76e67508bb9be98d6cdfba9b to your computer and use it in GitHub Desktop.
Rails API + React Server Deployment [Capistrano | Nginx | Passenger]

Rails API + React Server Deployment [Capistrano | Nginx | Passenger]

Prerequisites

OS : Ubuntu 18.04LTS

Initial Setup with deploy sudouser

Install rbenv-vars

Prepare the server

Update the system and install essential dependencies

# Dependencies
sudo apt install -y git zsh curl jq build-essential tklib zlib1g-dev libssl-dev libffi-dev libxml2 libxml2-dev libxslt1-dev libreadline-dev
sudo apt clean

# Oh my zsh
sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"

# NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | zsh

# Reload shell
zsh

# Install the latest stable version (currently v14.16.0)
nvm install --lts
nvm alias default 14.16.0

Install Ruby and rbenv

# Clean RVM et rbenv
rvm implode && sudo rm -rf ~/.rvm
rm -rf ~/.rbenv

# Install rbenv
git clone https://github.com/rbenv/rbenv.git ~/.rbenv

# Install Ruby-build
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

Update rbenv PATH

cd
nano .zshrc

# Add PATH to zshrc
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

# Reload shell
zsh

# Check rbenv
rbenv

Install Ruby and set version globally

# Install ruby
rbenv install 2.7.2

# Set version globally
rbenv global 2.7.2

# Reload shell
zsh

# Check version (ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux])
ruby -v

# Install gem for rails project
gem install rails rake bundler

POSTGRESql

Install Postgres and dependencies

sudo apt install -y postgresql postgresql-contrib libpq-dev

Create deploy user

sudo -u postgres psql --command "CREATE ROLE `whoami` LOGIN createdb;"

NGINX PASSENGER

Install Nginx and passenger module

# Install nginx server
sudo apt-get install nginx 

# Install passenger PGP key and HTTPS support for APT
sudo apt-get install -y dirmngr gnupg
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates

# Add Passenger API repository
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bionic main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update

# Install Passenger + Nginx module
sudo apt-get install -y libnginx-mod-http-passenger

sudo service nginx restart

Remove password requirment when restart Nginx for Capistrano.

sudo nano /etc/sudoers

# Add at the end of file
ALL ALL=NOPASSWD: /etc/init.d/nginx restart

Prepare deployment directory

Create the main directory

mkdir -p /home/deploy/www/your-app-name

# Create rbenv-vars
cd /home/deploy/www/your-app-name

touch .rbenv-vars
# Add SECRET_KEY_BASE and others Env

Create config files

mkdir -p shared/config
touch shared/config/master.key
touch shared/config/database.yml

# Set RAILS_ENV
cd
nano .zshrc

# Add at the end
export RAILS_ENV=production

Copy master.key content locally and past on server.

Configure shared/config/database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
production:
  <<: *default
  database: your_app_name_production
  username: deploy

Create Nginx log files

mkdir /home/deploy/www/your-app-name/logs
touch /home/deploy/www/your-app-name/logs/access.log
touch /home/deploy/www/your-app-name/logs/errors.log

Capistrano

Locally

Add to development group in Gemfile

group :development do
  gem 'capistrano', '~> 3.10', require: false
  gem 'capistrano3-nginx', '~> 2.0'
  gem 'capistrano-nvm', require: false
  gem 'capistrano-passenger' #, '>= 0.2.1'
  gem 'capistrano-rails', '~> 1.4', require: false
  gem 'capistrano-rbenv', '~> 2.1'
  gem 'capistrano-yarn'
end

Then run

bundle install
bundle exec cap install

Go to Capfile and uncomment

require "capistrano/rbenv"
require "capistrano/bundler"

Add this in config/deploy/production.rb

# Change with your server IP
server "11.11.111.1", user: "deploy", roles: %w{app db web}

Update config/deploy.rb :

# frozen_string_literal: true

# config valid for current version and patch releases of Capistrano
lock '~> 3.16.0'

set :application, 'your-app-name'
set :repo_url, 'git@github.com:USER/your-app-repo.git'

set :branch, 'main'

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, '/home/deploy/www/your-app-name'

# Default value for :linked_files is []
append :linked_files, 'config/database.yml', 'config/master.key'

# Default value for linked_dirs is []
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system', 'storage'

# Default value for keep_releases is 5
set :keep_releases, 5

# Rbenv
set :rbenv_type, :user # or :system, depends on your rbenv setup
set :rbenv_ruby, '2.7.2'

# Nvm/Node.JS
set :nvm_type, :user # or :system, depends on your nvm setup
set :nvm_node, 'v14.16.0'
set :nvm_map_bins, %w[node npm yarn]
set :yarn_flags, '--silent --no-progress'

# # Passenger
set :passenger_roles, :app
set :passenger_restart_runner, :sequence
set :passenger_restart_wait, 10
set :passenger_restart_limit, 2
set :passenger_restart_with_sudo, false
set :passenger_environment_variables, { RAILS_ENV: 'production' }
set :passenger_restart_command, 'passenger stop && passenger start --daemonize --address 127.0.0.1 --port 3000'
set :passenger_restart_options, -> { "#{deploy_to}/current" }

# Nginx
set :nginx_roles, :web
set :nginx_sudo_paths, []
set :nginx_sudo_tasks, ['nginx:restart']

# Used to build for optimized production
namespace :deploy do
  task :yarn_build do
    on roles fetch(:yarn_roles) do
      within fetch(:yarn_target_path, release_path) do
        execute fetch(:yarn_bin), 'install'
        execute fetch(:yarn_bin), 'build'
      end
    end
  end
  desc 'Restart application'
  task :restart do
    on roles fetch(:nginx_roles) do
      execute :sudo, '/etc/init.d/nginx restart'
    end
  end
  before 'symlink:release', :yarn_build
  after :finishing, :restart
end

Run the first deploy

cap production deploy

Server

Create Database

cd /home/deploy/www/your-app-name/current
rails db:create
rails db:migrate

Add yarn globally for react build script

npm install -g yarn

Nginx Configuration

Launch application with Passenger

cd /home/deploy/www/your-app-name/current
passenger start --daemonize --address 127.0.0.1 --port 3000

Configure Nginx

cd /etc/nginx/sites-available

# remove default if first app
sudo rm default

# create server conf for you app
sudo nano your-app-name

Update your-app-namefor HTTPS

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {

  root /home/deploy/www/your-app-name/current/public;

  index index.html;
  server_name domain.com;
  access_log /home/deploy/www/your-app-name/logs/access.log;
  error_log /home/deploy/www/your-app-name/logs/errors.log;
  # server_name localhost;
  passenger_app_root /home/deploy/www/your-app-name;
  passenger_enabled on;
  passenger_app_env production;
  client_max_body_size 20M;
  location ~* ^.+\.(jpeg|gif|png|jpg|webp) {
            proxy_pass http://127.0.0.1:3000;
            proxy_http_version 1.1;
  }
  location /api {
            # Insert your public app path
            proxy_pass http://127.0.0.1:3000;
            proxy_http_version 1.1;
            proxy_set_header X-Forwarded-Proto https;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_buffering off;
  }
  location / {
              # First attempt to serve request as file, then
              # as directory, then fall back to displaying a 404.
              try_files $uri $uri/ /index.html;
  }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/domain.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

}

server {
    if ($host = domain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


  listen 80;
  server_name domain.com;
  server_name localhost;
    return 404; # managed by Certbot
}

Active the new config

cd /etc/nginx/sites-enabled

# remove default if first app
sudo rm default

# Link conf file
sudo ln -s /etc/nginx/sites-available/your-app-name /etc/nginx/sites-enabled/
sudo service nginx restart

Locally

Edit Capfile and uncomment

require "capistrano/rails/migrations"
require "capistrano/nvm"
require 'capistrano/yarn'
require "capistrano/nginx"
require "capistrano/passenger"

Final deploy

cap production deploy

Server

Create alias for restart app

nano ~/.zshrc

# add at the end
alias restart-app="cd /home/deploy/www/your-app-name/current && passenger stop && passenger start --daemonize --address 127.0.0.1 --port 3000 && sudo /etc/init.d/nginx restart && cd"

Restart App

restart-app

MAGIE !

Sources

Create React App + Rails 6.0 + Custom Server Deployment — Part 2 Uploader and Custom Server Deployment

Passenger - Ubuntu Bionic

Rails Credentials

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment