Eth3rnit3
9/5/2019 - 11:12 AM

README.md

Server Configuration from scratch

My public ip : 51.15.204.137 Log in to your new server by ssh, use key/pair or password depending on the service you have chosen

ssh root@51.15.204.137

Now let's create a new user that we call deploy or whatever you want and add it to the sudo group

adduser deploy # Create a newuser and this password (wasn't afraid to use a complex password)
usermod -aG sudo deploy # Add 'deploy' user to sudo group

exit # Exit ssh sessions with root

Now log in with deploy user but first we can configure access for use key/pair instead of password

ssh-copy-id deploy@51.15.204.137 # Enter your password to use when creating 'deploy' user

ssh deploy@51.15.204.137 # Magic no password asked !!!

The first thing to do is to update the system and install some essential dependencies

sudo apt-get update # Update packages list
sudo apt-get upgrade # Update installed packages
sudo apt install -y git zsh curl jq build-essential tklib zlib1g-dev libssl-dev libffi-dev libxml2 libxml2-dev libxslt1-dev libreadline-dev # Install essential dependencies
sudo apt clean
sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" # Install oh-my-zsh for shell autocompletion, history and more

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | zsh # Install nvm to easyly manage node version

zsh # To reload shell
nvm install --lts # To install the latest stable version  (version is currently 10.16.3)
nvm alias default 10.16.3

Ruby installation with rbenv

rvm implode && sudo rm -rf ~/.rvm # Remove rvm if exist
rm -rf ~/.rbenv # Remove rbenv if exist

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

zsh # To reload config
rbenv # To check command

zsh: command not found: rbenv

On some linux versions, the PATH variable is not automatically updated.

Open the .zshrc with nano and add the following to the file

cd
nano .zshrc
export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

Now re-run

zsh
rbenv

Install the latest version or ruby

rbenv install 2.6.3 # Install ruby
# It's quite long, it can take up to 10min depending on the computers.

rbenv global 2.6.3 # Set version globally
zsh # Reload config
ruby -v # Check ruby version, it must be ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]

gem install rails rake bundler # Install basic gems to manage rails project

Let's now install a POSTGRESQL server or whatever else you want

sudo apt install -y postgresql postgresql-contrib libpq-dev # Installs postgresql and the necessary dependencies 
sudo -u postgres psql --command "CREATE ROLE `whoami` LOGIN createdb;" # Create a postgresql user named 'deploy'

Finally, we will install nginx and its module for passenger so that our ruby application can be interpreted and publicly available.

sudo apt-get install nginx # Install nginx server

# 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

sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger stretch main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update

sudo apt-get install -y libnginx-mod-http-passenger
sudo service nginx restart

You can read more (here for nginx and passenger integration)[https://www.phusionpassenger.com/library/walkthroughs/deploy/ruby/ownserver/nginx/oss/stretch/install_passenger.html]

sudo nano /etc/sudoers

so that capistrano can restart nginx, in sudoers file add the following line at the end

ALL ALL=NOPASSWD: /etc/init.d/nginx restart

The last thing we need to do before configuring capistrano and nginx is to create deployment directories

mkdir -p /home/deploy/www/rails-react-demo # Create directories

exit # To return on local machine

Capistrano configuration

go to Gemfile and add fallowing to development group

group :development do
  # Use Capistrano for deployment
  gem "capistrano", "~> 3.10", require: false
  gem "capistrano-rails", "~> 1.4", require: false
  gem 'capistrano-rbenv', '~> 2.1'
  gem 'capistrano-nvm', require: false
  gem 'capistrano-yarn'
  gem 'capistrano3-nginx', '~> 2.0'
  gem 'capistrano-passenger'
end

Then run

bundle install
bundle exec cap install

Go to Capfile and uncomment following

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

We will come back to uncomment capistrano/rails/migrations after the first deployment.

Go to config/deploy/production.rb and add this line. Of course, change the server address with yours

server "51.15.204.137", user: "deploy", roles: %w{app db web}

Now go to config/deploy.rb and update it like this

# config valid for current version and patch releases of Capistrano
lock "~> 3.11.0"

set :application, "rails-react-demo"
set :repo_url, "git@github.com:Eth3rnit3/rails-react-demo.git"

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, "/home/deploy/www/rails-react-demo"

# 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.6.3'

# Nvm/Node.JS
set :nvm_type, :user # or :system, depends on your nvm setup
set :nvm_node, 'v10.16.3'
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

To avoid errors during the first deployment, we will reconnect to the server and go to the deployment directory, we will create a shared directory (used by capistrano) and a config directory inside. In the config directory, we will add the two linked files. master.key and database.yml which are as follows but which you can modify if necessary

ssh deploy@51.15.204.137
cd www/rails-react-demo
mkdir -p shared/config
touch shared/config/master.key
touch shared/config/database.yml

# Make RAILS_ENV
cd
nano .zshrc # add export RAILS_ENV=production or type it everytime

master.key is generated by rails, copy locally and paste on server database.yml is quite personal, it's up to you to set it up as needed. However, if you have followed this tutorial, here is the configuration adapted

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

Next create nginx log files

mkdir /home/deploy/www/rails-react-demo/logs
touch /home/deploy/www/rails-react-demo/logs/access.log
touch /home/deploy/www/rails-react-demo/logs/errors.log

Return locally We are now able to run the first deploy

cap production deploy

Normally you should not get an error message but if this is the case capistrano is rather explicit, you should be able to fix it simply

Let's go back to the server and go to the deploy directory

cd /home/deploy/www/rails-react-demo/current
rails db:create
rails db:migrate
# Created database 'rails_react_demo_production'

Add yarn too for react build script

npm install -g yarn

It's time to configure nginx to forward our application to the public. First, let's launch the application with passenger

cd /home/deploy/www/rails-react-demo/current
passenger start --daemonize --address 127.0.0.1 --port 3000

And configure nginx

cd /etc/nginx/sites-available
sudo nano rails-react-demo

Add the following config

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
server {
  listen 80;

  root /home/deploy/www/rails-react-demo/current/public;

  index index.html;
  server_name 51.15.204.137;
  access_log /home/deploy/www/rails-react-demo/logs/access.log;
  error_log /home/deploy/www/rails-react-demo/logs/errors.log;
  server_name localhost;
  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 Host $http_host;
            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 /index.html;
  }
}

Active the new config

cd /etc/nginx/sites-enabled
sudo rm default
sudo ln -s /etc/nginx/sites-available/rails-react-demo ./rails-react-demo
sudo service nginx restart

Return to Capfile, uncomment and add

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

And finally the final deployment!, just run

cap production deploy

You can follow the deployment until the end. if all is well, past (which should be the case) you can go to the ip address of the server, and you will see your application appear. Open the console and click on the fetch button, you should see an answer appear in the console. If you receive an error 500, it is probably because rails has been badly restarted.

Don't panic, do the following

ssh deploy@51.15.204.137
cd /home/deploy/www/rails-react-demo/current
passenger stop && passenger start --daemonize --address 127.0.0.1 --port 3000
sudo /etc/init.d/nginx restart

Refresh your browser and click again on the fetch button

Little bonus

You can add this command shortcut as an alias into .zshrc file

alias restart-app="cd /home/deploy/www/rails-react-demo/current && passenger stop && passenger start --daemonize --address 127.0.0.1 --port 3000 && sudo /etc/init.d/nginx restart && cd"