Skip to content

Instantly share code, notes, and snippets.

@anko20094
Last active June 4, 2023 09:52
Show Gist options
  • Save anko20094/6e713141ec16e98aef4956d046001e98 to your computer and use it in GitHub Desktop.
Save anko20094/6e713141ec16e98aef4956d046001e98 to your computer and use it in GitHub Desktop.
Set up server for a Rails application. Railas 7, Postgresql, Esbuild, Propshaft, Puma, Nginx, RVM
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
DB_USERNAME=NAME_OF_USER_FOR_DB_IN_DEVELOPMENT_ENV
DB_PASSWORD=PASSWORD_OF_USER_FOR_DB_IN_DEVELOPMENT_ENV
PRODUCTION_USERNAME=NAME_OF_USER_FOR_DB
PRODUCTION_PASSWORD=PASSWORD_OF_USER_FOR_DB
Note: Replace UPPERCASE words with your own setup details.
Note 2: I use vim to edit files, you can and should replace vim with nano or any other editor of choice if you're not familiar with it.
Note 3: Always use random and long passwords, don't share them between applications and don't lose them. Also never commit unencrypted secrets to public repos.
1) Good practice to update packages
sudo apt-get update
2) Install nginx
sudo apt-get install curl git-core nginx -y
3) INSTALL POSTGRESS
sudo apt-get install postgresql postgresql-contrib libpq-dev
4) Log in to the postgres console
sudo -u postgres psql
5) Create a user with rights and a database
create database NAME_OF_APPLICATION_production;
\l
create user NAME_OF_USER_FOR_DB with encrypted password 'PASSWORD_OF_USER_FOR_DB';
grant all privileges on database NAME_OF_APPLICATION_production to NAME_OF_USER_FOR_DB;
\q
6) Change authentication from peer to md 5
sudo vim /etc/postgresql/14/main/pg_hba.conf
local all all peer -> md5
7) Restart postgres for the changes to take effect
sudo service postgresql restart
sudo service postgresql status
8) Add the necessary tools: Nodejs + NPM + yarn
curl -sL https://deb.nodesource.com/setup_18.x -o /tmp/nodesource_setup.sh
sudo bash /tmp/nodesource_setup.sh
sudo apt install nodejs
npm install --global yarn
node -v
npm -v
yarn -v
9) Install redis and add to autoload
sudo apt update
sudo apt install redis-server
sudo nano /etc/redis/redis.conf
sudo systemctl enable redis-server
10) Install and configure RVM
sudo apt-get install software-properties-common
sudo apt-add-repository -y ppa:rael-gc/rvm
sudo apt-get update
sudo apt-get install rvm
sudo usermod -a -G rvm $USER
echo 'source "/etc/profile.d/rvm.sh"' >> ~/.bashrc
### exit and connect to the server once again
rvm install "ruby-3.2.2"
rvm use ruby-3.2.2@NAME_OF_APPLICATION --create --default
gem install rails -v '7.0.5' -V
### if .rvm/bin/rvm not found error -->>
mkdir .rvm
mkdir .rvm/bin
ln -s /usr/share/rvm/bin/rvm .rvm/bin/rvm
11) Generate SSH and put into the application repository
ssh-keygen -t rsa
cat ~/.ssh/id_rsa.pub
11) Set your ssh to authorised keys
cat ~/.ssh/id_rsa.pub | ssh root@IP_OF_SERVER 'cat >> ~/.ssh/authorized_keys'
#if it still ask password when you try connect to server execute next in local terminal:
eval `ssh-agent -s`
ssh-add ~/.ssh/id_rsa
12) Run the command from the local terminal:
cap production puma:systemd:config
cap production puma:make_dirs
cap production deploy:initial
13) Remove defualt nginx settings
sudo rm /etc/nginx/sites-enabled/default
14) Copy nginx settings for an application
sudo ln -nfs "/home/root/apps/NAME_OF_APPLICATION/current/config/nginx.conf" "/etc/nginx/sites-enabled/NAME_OF_APPLICATION"
15) You need to restart nginx for the changes to take effect
sudo service nginx restart
# config\cable.yml
# frozen_string_literal: true
development:
adapter: redis
url: redis://localhost:6379/1
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: NAME_OF_APPLICATION_production
# frozen_string_literal: true
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/scm/git'
install_plugin Capistrano::SCM::Git
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/puma'
install_plugin Capistrano::Puma
install_plugin Capistrano::Puma::Systemd
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
# config/database.yml
default: &default
adapter: postgresql
host: <%= ENV['POSTGRES_HOST'] %>
port: <%= ENV['POSTGRES_PORT'] %>
username: <%= ENV['DB_USERNAME'] %>
password: <%= ENV['DB_PASSWORD'] %>
encoding: utf8
reconnect: true
pool: 5
development:
<<: *default
database: NAME_OF_APPLICATION_development
test:
<<: *default
database: NAME_OF_APPLICATION_test
production:
<<: *default
database: NAME_OF_APPLICATION_production
username: <%= ENV["PRODUCTION_USERNAME"] %>
password: <%= ENV["PRODUCTION_PASSWORD"] %>
# config/deploy.rb
# frozen_string_literal: true
# config valid for current version and patch releases of Capistrano
lock '~> 3.17.2'
# Change these
server 'IP_OF_SERVER', port: 22, roles: %i[web app db], primary: true
set :application, 'NAME_OF_APPLICATION'
set :repo_url, 'URL_OF_APPLICATION_REPOSITORY'
set :branch, 'master'
set :user, 'root'
set :deploy_to, "/home/#{fetch(:user)}/apps/#{fetch(:application)}"
set :ssh_options, { forward_agent: true, user: fetch(:user), keys: %w[~/.ssh/id_rsa.pub] }
set :pty, true
set :use_sudo, false
set :stage, :production
set :deploy_via, :remote_cache
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 :puma_preload_app, true
set :puma_worker_timeout, nil
set :puma_init_active_record, true
set :puma_enable_socket_service, true
# Issue with propshaft as asset pipwlinw
# See: https://github.com/capistrano/rails/issues/257
# Workaround
set :assets_manifests, lambda {
[release_path.join('public', fetch(:assets_prefix), '.manifest.json')]
}
append :linked_files, *%w[config/master.key config/database.yml .env]
set :linked_dirs, %w[log tmp/pids tmp/cache tmp/sockets vendor/bundle public/assets public/uploads]
# config\nginx.conf
upstream app {
# Path to Puma SOCK file, as defined previously
server unix:///home/root/apps/NAME_OF_APPLICATION/shared/tmp/sockets/NAME_OF_APPLICATION-puma.sock;
}
server {
listen 80;
server_name DOMAIN_FOR_AN_APPLICATION_AND\OR_IP_OF_SERVER;
root /home/root/apps/NAME_OF_APPLICATION/current/public;
try_files $uri/index.html $uri @app;
location @app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location /cable {
proxy_pass http://app/cable;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
# config/environments/production.rb
Rails.application.configure do
# ............
config.action_cable.allowed_request_origins = ["http://IP_OF_SERVER"]
# ............
config.assets.compile = false
# ............
end
# config/routes.rb
Rails.application.routes.draw do
mount ActionCable.server, at: '/cable'
# .............
end
# lib/capistrano/tasks/setup.rake
# frozen_string_literal: true
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
namespace :check do
before :linked_files, :set_master_key do
on roles(:app), in: :sequence, wait: 10 do
unless test("[ -f #{shared_path}/config/master.key ]")
upload! 'config/master.key',
"#{shared_path}/config/master.key"
end
unless test("[ -f #{shared_path}/config/database.yml ]")
upload! 'config/database.yml',
"#{shared_path}/config/database.yml"
end
unless test("[ -f #{shared_path}/.env ]")
upload! '.env',
"#{shared_path}/.env"
end
end
end
end
desc 'Make sure local git is in sync with remote.'
task :check_revision do
on roles(:app) do
# Update this to your branch name: master, main, etc. Here it's master
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
desc 'reload the database with seed data'
task :seed do
puts "\n=== Seeding Database ===\n"
on primary :db do
within current_path do
with rails_env: fetch(:stage) do
execute :rake, 'db:seed'
end
end
end
end
before :starting, :check_revision
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment