How to setup rails, puma and nginx
Let’s suppose we have VPS server with ubuntu installed, we have developed a rails application that is ready to be deployed. We want to run the app in production mode.
At the end of this tutorial we will have the app that uses puma and revealed to the world via nginx.
Let’s start!
Make sure you have installed following packages on VPS:
sudo apt-get install gcc g++ make libssl-dev sqlite3 libsqlite3-dev libpq-dev postgresql
Open Gemfile
and add following gems:
group :development do
gem 'capistrano', '~> 3.8'
gem 'capistrano-lets-encrypt'
gem 'capistrano-rbenv', '~> 2.0'
gem 'capistrano3-puma'
gem 'capistrano-rails'
gem 'sshkit-sudo'
end
Generate capistrano files:
$ bundle exec cap install
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified
Uncomment and add these lines in Capfile
:
require "capistrano/rbenv"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
require "capistrano/bundler"
require 'capistrano/puma'
require 'capistrano/puma/jungle'
require 'capistrano/puma/workers'
require 'capistrano/puma/nginx'
require 'capistrano/lets-encrypt'
require 'sshkit/sudo'
Now trarget capistrano to your VPS server with editing of config\deploy\production.rb
:
server 'www.my_server.com', user: 'my_vps_login', roles: %w[app db web], primary: true
set :lets_encrypt_domains, 'www.my_server.com'
Nice. Now we need to edit config\deploy.rb
file. Please add these lines:
set :application, "my_app"
set :repo_url, "git@github.com:login/my_app.git"
set :branch, :master
set :deploy_to, '/home/my_vps_login/projects/my_app'
append :linked_files, "puma.conf"
append :linked_dirs, 'log', 'tmp/pids', 'tmp/cache', 'tmp/puma', 'tmp/sockets', 'public/system', 'certs'
set :rails_env, 'production'
set :puma_state, "#{shared_path}/tmp/puma/state"
set :puma_pid, "#{shared_path}/tmp/puma/pid"
set :puma_preload_app, true
set :puma_conf, "#{shared_path}/puma.conf"
set :lets_encrypt_roles, :web
set :lets_encrypt_user, 'my_vps_login'
set :lets_encrypt_email, 'my_email'
set :lets_encrypt_account_key, "~/#{fetch(:lets_encrypt_email)}.account_key.pem"
set :lets_encrypt_challenge_public_path, "#{release_path}/public"
set :lets_encrypt_output_path, '/etc/nginx/ssl'
set :lets_encrypt_local_output_path, '~/certs'
set :lets_encrypt_days_valid, 90
Ok, let’s try to deploy the app:
$ bundle exec cap production deploy
...
ERROR linked file ~/projects/my_app/shared/puma.conf does not exist on www.my_server.com
...
You have to get errors. Let me explain a schema we’re going to use.
Normally if you use Rails 5 it uses puma
as default server. When you start an app with rails s
command puma
reads config\puma.rb
configuration file and gets started.
Since we’re going to use puma
on VPS things could be a bit different and we need different configuration file.
Thus in order to not mess with default configuration file we will use puma.conf
. This file will be used only on VPS.
Run bundle exec cap production puma:config
command it will create and uploade puma.conf
to shared folder.
Or you can log on you VPS and create ~/projects/my_app/shared/puma.conf
and copy this:
directory '/home/my_vps_login/projects/my_app/current'
rackup '/home/my_vps_login/projects/my_app/current/config.ru'
threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }.to_i
threads threads_count, threads_count
environment ENV.fetch('RAILS_ENV') { 'production' }
prune_bundler
preload_app!
plugin :tmp_restart
pidfile '/home/my_vps_login/projects/my_app/shared/tmp/puma/pid'
state_path '/home/my_vps_login/projects/my_app/shared/tmp/puma/state'
bind 'unix:///home/my_vps_login/projects/my_app/shared/tmp/sockets/puma.sock'
bind 'tcp://127.0.0.1:3000'
activate_control_app
Try again. You should get something alike below:
$ bundle exec cap production deploy
...
puma:start
using conf file ~/projects/my_app/shared/puma.conf
01 $HOME/.rbenv/bin/rbenv exec bundle exec puma -C ~/projects/my_app/shared/puma.conf --daemon
01 Puma starting in single mode...
01
01 * Version 3.9.1 (ruby 2.4.1-p111), codename: Private Caller
01
01 * Min threads: 5, max threads: 5
01
01 * Environment: production
01
01 * Daemonizing...
...
You can control your puma server with commands:
$ bundle exec cap production puma:status
$ bundle exec cap production puma:start
$ bundle exec cap production puma:stop
$ bundle exec cap production puma:restart
Now we need to care about that our app is get started on system boot. For this we use jungle.
Do not use cap production puma:jungle:install
instead log in on VPS server. Download two bash scripts:
wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/init.d/puma
wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/init.d/run-puma
And follow this instruction.
Now start out app
sudo /etc/init.d/puma start
Now is turn of nginx. It’s easy. Just run:
bundle exec cap production puma:nginx_config
You will get /etc/nginx/sites-available/my_app_production
conf file. Edit it as you needed.
Now restart nginx
sudo /etc/init.d/nginx restart
Your app should be up and ready. Enjoy!