Skip to content

Instantly share code, notes, and snippets.

@deHelden
Last active June 9, 2024 00:31
Show Gist options
  • Save deHelden/d18700a0996cd09d9321522c6c6af5a3 to your computer and use it in GitHub Desktop.
Save deHelden/d18700a0996cd09d9321522c6c6af5a3 to your computer and use it in GitHub Desktop.
Deploy Rails 7.0.4.2 to VPS(DigitalOcean Ubuntu 20). Nginx, Puma, Capistrano3, PostgreSQL, Rbenv.

SETUP VPS

based on DigitalOcean guide

Create local project

local$ rails new appname -T -d postgresql
local$ rails g scaffold Story title:string body:text
local$ rails db:migrate

Go to Step 1.1 "Create deploy user" (remote root$)


Go to Step 1.2 "Setup rbenv ruby rails nginx postgresql" (remote deploy$)


Setup ssh for GitHub

deploy$ ssh-keygen -t rsa # Add the newly created public key (~/.ssh/id_rsa.pub) to your repository’s deployment keys

Go to step 2 "Setup local Capistrano"


Go to step 3 "Setup local Capfile"


Go to step 4 "Setup local deploy.rb file"


Go to step 5 "Setup local nginx.conf file"


DEPLOYING RAILS APP

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

sharing creds based on Medium column

Copy config/master.key from local filesystem to the production server under appname/shared/config/master.key

local$ cap production deploy:initial

Reboot Nginx

deploy$ sudo rm /etc/nginx/sites-enabled/default
deploy$ sudo ln -nfs "/home/deploy/apps/appname/current/config/nginx.conf" "/etc/nginx/sites-enabled/appname"
deploy$ sudo service nginx restart

configure firewall

root# apt update && apt upgrade

root# adduser deploy
root# gpasswd -a deploy sudo

# optional
root# vim /etc/ssh/sshd_config
# CHANGE `PermitRootLogin no` 
root# service ssh restart

root# rsync --archive --chown=deploy:deploy ~/.ssh /home/sammy

# check user could log in 

root# exit
deploy$ sudo apt -y install git-core curl autoconf bison zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev libffi-dev libz-dev libgdbm-dev libncurses5-dev


## For rails 6 also should be installed node.js & yarn
## God forbid you should do it with nvm. RIP this way

deploy$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
deploy$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
deploy$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc &&  source ~/.bashrc 
deploy$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
deploy$ rbenv install 2.6.3 && rbenv global 2.6.3
deploy$ echo "gem: --no-document" > ~/.gemrc
deploy$ gem install bundler
deploy$ gem install rails -v 7.0.4.2 --no-document

deploy$ sudo apt install curl git-core nginx -y

deploy$ sudo apt -y install postgresql postgresql-contrib libpq-dev
deploy$ sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
deploy$ wget -qO- https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo tee /etc/apt/trusted.gpg.d/pgdg.asc &>/dev/null

deploy# sudo apt update -y

deploy$ sudo apt install postgresql postgresql-client -y
deploy$ sudo -u postgres psql -c "SELECT version();"

#  Upgrade init Postgres from 14 to 15 : https://gorails.com/guides/upgrading-postgresql-version-on-ubuntu-server

deploy$ sudo -u postgres createuser deploy -s
deploy$ sudo -u postgres psql
postgres=# \password deploy
postgres=# create database appname;
postgres=# \q
ADD_TO_Gemfile:
group :development do
gem 'capistrano', require: false
gem 'capistrano-rbenv', require: false
gem 'capistrano-rails', require: false
gem 'capistrano-bundler', require: false
gem 'capistrano3-puma', require: false
end
RUN:
cap install
# Load DSL and set up stages
require "capistrano/setup"
# Include default deployment tasks
require "capistrano/deploy"
require "capistrano/rbenv"
require "capistrano/bundler"
require "capistrano/rails"
require "capistrano/puma"
install_plugin Capistrano::Puma
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
# Change these
server 'your_server_ip', port: your_port_num, roles: [:web, :app, :db], primary: true # port_num is optional can be removed
set :repo_url, '[email protected]:username/appname.git'
set :application, 'appname'
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, 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}
set :linked_files, %w{config/master.key}
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
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

deploy# sudo vim /etc/nginx/nginx.conf # update user with actual user FE: xxx-data->deploy

Create config/nginx.conf in your Rails project directory add the following to it (again, replacing with your parameters):


upstream puma {
  server unix:///home/deploy/apps/appname/shared/tmp/sockets/appname-puma.sock;
}

server {
  listen 80 default_server deferred;
  # server_name example.com;

  root /home/deploy/apps/appname/current/public;
  access_log /home/deploy/apps/appname/current/log/nginx.access.log;
  error_log /home/deploy/apps/appname/current/log/nginx.error.log info;

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

  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;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 10M;
  keepalive_timeout 10;
}
Capistrano::Rails::Db
cap production deploy:db:abort_if_pending_migrations # Run rake db:abort_if_pending_migrations
cap production deploy:db:create # Run rake db:create
cap production deploy:db:drop # Run rake db:drop
cap production deploy:db:migrate # Run rake db:migrate Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
cap production deploy:db:migrate:down # Run rake db:migrate:down Run the "down" for a given migration VERSION
cap production deploy:db:migrate:redo # Run rake db:migrate:redo Rollback the database one migration and re migrate up (options: STEP=x, VERSION=x)
cap production deploy:db:migrate:reset # Run rake db:migrate:reset Reset your database using your migrations
cap production deploy:db:migrate:status # Run rake db:migrate:status Display status of migrations
cap production deploy:db:migrate:up # Run rake db:migrate:up Run the "up" for a given migration VERSION
cap production deploy:db:reset # Run rake db:reset Drop and recreate the database from db/schema.rb and load the seeds
cap production deploy:db:rollback # Run rake db:rollback Roll the schema back to the previous version (specify steps w/ STEP=n)
cap production deploy:db:seed # Run rake db:seed Load the seed data from db/seed.rb
cap production deploy:db:setup # Run rake db:setup Create the database, load the schema, and initialize with the seed data
cap production deploy:db:version # Run rake db:version Retrieve the current schema version number
Capistrano::Nginx
nginx:setup # creates /etc/nginx/sites-available/APPLICATION.conf and links it to /etc/nginx/sites-enabled/APPLICATION.conf
nginx:stop # invokes service nginx stop on server
nginx:start # invokes service nginx start on server
nginx:restart # invokes service nginx restart on server
nginx:reload # invokes service nginx reload on server
nginx:force-reload # invokes service nginx force-reload on server
nginx:enable_site # creates symlink in sites-enabled directory
nginx:disable_site # removes symlink from sites-enabled directory
Capistrano::Rails::Console
cap production rails:console # This will add a task rails:console
cap production rails:console sandbox=1 # You can also start a sandbox session
For more commands run:
cap -T
# create service file
# sudo vim /etc/systemd/system/puma.service
#
# than reload daemon
# sudo systemctl daemon-reload 
#
# enable puma service 
# sudo systemctl enable puma
#
# install puma as gem
# gem install puma
#
# Get number of CPU cores
# grep -c processor /proc/cpuinfo
# 
# Set number of puma workers as CPU cores
# vim config/puma.rb
#
# debug by running start command from shell
# /home/deploy/.rbenv/shims/puma -C /srv/deploy/current/config/puma.rb

[Unit]
Description=Puma HTTP Server
After=network.target

[Service]
Type=simple
WorkingDirectory=/srv/deploy/current
Environment=RAILS_ENV=production
Environment=RAILS_MASTER_KEY=insert_master_key

ExecStart=/home/deploy/.rbenv/shims/puma -C /srv/deploy/current/config/puma.rb
ExecStop=/home/deploy/.rbenv/shims/puma -S /srv/deploy/current/config/puma.rb

Restart=always
KillMode=process

[Install]
WantedBy=multi-user.target
@juansecaro
Copy link

I have tried step by step

At some point it tries to restart the puma sytemd, but it doesn't exist. Also the whole puma setting process is a nightmare.

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