Skip to content

Instantly share code, notes, and snippets.

@natew
Last active March 16, 2022 06:35
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save natew/6207594 to your computer and use it in GitHub Desktop.
Save natew/6207594 to your computer and use it in GitHub Desktop.
Puma + Nginx + Capistrano
require 'bundler/capistrano'
require 'capistrano_colors'
load 'deploy/assets'
# ssh forwarding and shell
set :default_run_options, { :pty => true }
set :ssh_options, { :forward_agent => true }
set :scm_verbose, true
set :scm, :git
set :branch, 'master'
set :keep_releases, 2
set :use_sudo, false
set :deploy_via, :remote_cache
set :user, "USERNAME"
set :application, "APP_NAME"
set :domain, "IP"
set :repository, "ssh://#{user}@#{domain}/var/git/#{application}.git"
set :deploy_to, "/var/www/#{application}/web"
set :rails_env, "production"
# chruby
# set :bundle_flags, "--verbose"
set :ruby_version, "rbx"
set :chrub_script, "/usr/local/share/chruby/chruby.sh"
set :set_ruby_cmd, ". #{chrub_script} && chruby #{ruby_version}"
set(:bundle_cmd) { "#{set_ruby_cmd} && RAILS_ENV=#{rails_env} exec bundle" }
# Roles
role :web, domain
role :app, domain
role :db, domain, :primary => true # This is where Rails migrations will run
after 'deploy:update_code', 'deploy:migrate'
after 'deploy:update', 'deploy:symlink_attachments'
after 'deploy:update', 'deploy:symlink_tmp'
after 'deploy:update', 'deploy:clear_caches'
after 'deploy:update', 'deploy:cleanup'
# Run rake tasks
def run_rake(task, options={}, &block)
command = "cd #{latest_release} && #{bundle_cmd} rake #{task}"
run(command, options, &block)
end
namespace :puma do
task :start, :except => { :no_release => true } do
run "/etc/init.d/puma start #{application}"
end
after "deploy:start", "puma:start"
task :stop, :except => { :no_release => true } do
run "/etc/init.d/puma stop #{application}"
end
after "deploy:stop", "puma:stop"
task :restart, roles: :app do
run "/etc/init.d/puma restart #{application}"
end
after "deploy:restart", "puma:restart"
end
namespace :deploy do
task :symlink_attachments do
run "ln -nfs #{shared_path}/attachments #{release_path}/public/attachments"
end
task :symlink_tmp do
run "rm -rf #{release_path}/tmp"
run "ln -nfs #{shared_path}/tmp #{release_path}/tmp"
run "chmod 775 #{shared_path}/tmp"
end
task :clear_caches do
run "echo 'flush_all' | nc localhost 11211" # memcached
run_rake "tmp:cache:clear >/dev/null 2>&1"
end
end
upstream APP_puma {
server unix:///var/www/APP/web/current/tmp/puma/puma.sock;
}
server {
listen 80 default;
server_name APP.com www.APP.com;
root /var/www/APP/web/current/public;
location ~ ^/(assets|fonts|images|attachments)/?|favicon.ico {
root /var/www/APP/web/current/public;
expires max;
break;
}
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect off;
proxy_pass http://APP_puma;
}
}
#! /bin/bash
### BEGIN INIT INFO
# Provides: puma
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Example initscript
# Description: This file should be used to construct scripts to be
# placed in /etc/init.d.
### END INIT INFO
# Author: Darío Javier Cravero <dario@exordo.com>
#
# Do NOT "set -e"
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/usr/local/bin:/usr/local/sbin/:/sbin:/usr/sbin:/bin:/usr/bin
DESC="Puma rack web server"
NAME=puma
DAEMON=$NAME
SCRIPTNAME=/etc/init.d/$NAME
CONFIG=/etc/puma.conf
JUNGLE=`cat $CONFIG`
RUNPUMA=/usr/local/bin/run-puma
do_chruby() {
. /usr/local/share/chruby/chruby.sh && chruby rbx
}
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
#
# Function that starts the jungle
#
do_start() {
log_daemon_msg "=> Running the jungle..."
for i in $JUNGLE; do
dir=`echo $i | cut -d , -f 1`
user=`echo $i | cut -d , -f 2`
config_file=`echo $i | cut -d , -f 3`
if [ "$config_file" = "" ]; then
config_file="$dir/config/puma.rb"
fi
log_file=`echo $i | cut -d , -f 4`
if [ "$log_file" = "" ]; then
log_file="$dir/log/puma.log"
fi
do_start_one $dir $user $config_file $log_file
done
}
do_start_one() {
PIDFILE=$1/tmp/puma/pid
if [ -e $PIDFILE ]; then
PID=`cat $PIDFILE`
# If the puma isn't running, run it, otherwise restart it.
if [ "`ps -A -o pid= | grep -c $PID`" -eq 0 ]; then
do_start_one_do $1 $2 $3 $4
else
do_restart_one $1
fi
else
do_start_one_do $1 $2 $3 $4
fi
}
do_start_one_do() {
log_daemon_msg "--> Woke up puma $1"
log_daemon_msg "user $2"
log_daemon_msg "log to $4"
start-stop-daemon --verbose --start --chdir $1 --chuid $2 --background --exec $RUNPUMA -- $1 $3 $4
}
#
# Function that stops the jungle
#
do_stop() {
log_daemon_msg "=> Putting all the beasts to bed..."
for i in $JUNGLE; do
dir=`echo $i | cut -d , -f 1`
do_stop_one $dir
done
}
#
# Function that stops the daemon/service
#
do_stop_one() {
log_daemon_msg "--> Stopping $1"
PIDFILE=$1/tmp/puma/pid
STATEFILE=$1/tmp/puma/state
if [ -e $PIDFILE ]; then
PID=`cat $PIDFILE`
if [ "`ps -A -o pid= | grep -c $PID`" -eq 0 ]; then
log_daemon_msg "---> Puma $1 isn't running."
else
log_daemon_msg "---> About to kill PID `cat $PIDFILE`"
do_chruby
pumactl --state $STATEFILE stop
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE $STATEFILE
fi
else
log_daemon_msg "---> No puma here..."
fi
return 0
}
#
# Function that restarts the jungle
#
do_restart() {
for i in $JUNGLE; do
dir=`echo $i | cut -d , -f 1`
do_restart_one $dir
done
}
#
# Function that sends a SIGUSR2 to the daemon/service
#
do_restart_one() {
PIDFILE=$1/tmp/puma/pid
i=`grep $1 $CONFIG`
dir=`echo $i | cut -d , -f 1`
if [ -e $PIDFILE ]; then
log_daemon_msg "--> About to restart puma $1"
do_chruby
pumactl --state $dir/tmp/puma/state restart
# kill -s USR2 `cat $PIDFILE`
# TODO Check if process exist
else
log_daemon_msg "--> Your puma was never playing... Let's get it out there first"
user=`echo $i | cut -d , -f 2`
config_file=`echo $i | cut -d , -f 3`
if [ "$config_file" = "" ]; then
config_file="$dir/config/puma.rb"
fi
log_file=`echo $i | cut -d , -f 4`
if [ "$log_file" = "" ]; then
log_file="$dir/log/puma.log"
fi
do_start_one $dir $user $config_file $log_file
fi
return 0
}
#
# Function that statuss the jungle
#
do_status() {
for i in $JUNGLE; do
dir=`echo $i | cut -d , -f 1`
do_status_one $dir
done
}
#
# Function that sends a SIGUSR2 to the daemon/service
#
do_status_one() {
PIDFILE=$1/tmp/puma/pid
i=`grep $1 $CONFIG`
dir=`echo $i | cut -d , -f 1`
if [ -e $PIDFILE ]; then
log_daemon_msg "--> About to status puma $1"
do_chruby
pumactl --state $dir/tmp/puma/state stats
# kill -s USR2 `cat $PIDFILE`
# TODO Check if process exist
else
log_daemon_msg "--> $1 isn't there :(..."
fi
return 0
}
do_add() {
str=""
# App's directory
if [ -d "$1" ]; then
if [ "`grep -c "^$1" $CONFIG`" -eq 0 ]; then
str=$1
else
echo "The app is already being managed. Remove it if you want to update its config."
exit 1
fi
else
echo "The directory $1 doesn't exist."
exit 1
fi
# User to run it as
if [ "`grep -c "^$2:" /etc/passwd`" -eq 0 ]; then
echo "The user $2 doesn't exist."
exit 1
else
str="$str,$2"
fi
# Config file
if [ "$3" != "" ]; then
if [ -e $3 ]; then
str="$str,$3"
else
echo "The config file $3 doesn't exist."
exit 1
fi
fi
# Log file
if [ "$4" != "" ]; then
str="$str,$4"
fi
# Add it to the jungle
echo $str >> $CONFIG
log_daemon_msg "Added a Puma to the jungle: $str. You still have to start it though."
}
do_remove() {
if [ "`grep -c "^$1" $CONFIG`" -eq 0 ]; then
echo "There's no app $1 to remove."
else
# Stop it first.
do_stop_one $1
# Remove it from the config.
sed -i "\\:^$1:d" $CONFIG
log_daemon_msg "Removed a Puma from the jungle: $1."
fi
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
if [ "$#" -eq 1 ]; then
do_start
else
i=`grep $2 $CONFIG`
dir=`echo $i | cut -d , -f 1`
user=`echo $i | cut -d , -f 2`
config_file=`echo $i | cut -d , -f 3`
if [ "$config_file" = "" ]; then
config_file="$dir/config/puma.rb"
fi
log_file=`echo $i | cut -d , -f 4`
if [ "$log_file" = "" ]; then
log_file="$dir/log/puma.log"
fi
do_start_one $dir $user $config_file $log_file
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
if [ "$#" -eq 1 ]; then
do_stop
else
i=`grep $2 $CONFIG`
dir=`echo $i | cut -d , -f 1`
do_stop_one $dir
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
# TODO Implement.
log_daemon_msg "Status $DESC" "$NAME"
if [ "$#" -eq 1 ]; then
do_status
else
i=`grep $2 $CONFIG`
dir=`echo $i | cut -d , -f 1`
do_status_one $dir
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
restart)
log_daemon_msg "Restarting $DESC" "$NAME"
if [ "$#" -eq 1 ]; then
do_restart
else
i=`grep $2 $CONFIG`
dir=`echo $i | cut -d , -f 1`
do_restart_one $dir
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
add)
if [ "$#" -lt 3 ]; then
echo "Please, specifiy the app's directory and the user that will run it at least."
echo " Usage: $SCRIPTNAME add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log"
echo " config and log are optionals."
exit 1
else
do_add $2 $3 $4 $5
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
remove)
if [ "$#" -lt 2 ]; then
echo "Please, specifiy the app's directory to remove."
exit 1
else
do_remove $2
fi
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
*)
echo "Usage:" >&2
echo " Run the jungle: $SCRIPTNAME {start|stop|status|restart}" >&2
echo " Add a Puma: $SCRIPTNAME add /path/to/app user /path/to/app/config/puma.rb /path/to/app/config/log/puma.log"
echo " config and log are optionals."
echo " Remove a Puma: $SCRIPTNAME remove /path/to/app"
echo " On a Puma: $SCRIPTNAME {start|stop|status|restart} PUMA-NAME" >&2
exit 3
;;
esac
:
#!/usr/bin/env puma
basedir = '/var/www/2u/web/current'
directory "#{basedir}"
environment 'production'
daemonize true
bind "unix://#{basedir}/tmp/puma/puma.sock"
pidfile "#{basedir}/tmp/puma/pid"
state_path "#{basedir}/tmp/puma/state"
threads 4, 48
# stdout_redirect "#{basedir}/shared/log/stdout", "#{basedir}/shared/log/stderr"
# workers 2
# on_worker_boot do
# ActiveSupport.on_load(:active_record) do
# ActiveRecord::Base.establish_connection
# end
# end
preload_app!
activate_control_app
#!/bin/zsh
app=$1; config=$2; log=$3;
source /usr/local/share/chruby/chruby.sh && chruby rbx && cd $app && puma --debug -C $config 2>&1 >> $log
@sumanranjanpanda
Copy link

Hi Nate,

Could you please help me to configure using capistrano 3?

Here are my application information

  1. Built using Ruby 2.0.0 and Rails 3.2.19
  2. Deployed to AWS ec2 instance
  3. Running on nginx and puma server

I was manually deploying the changes to ec2, but now I want to use capistrano 3 for auto deployment.

Best,
Suman

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