Skip to content

Instantly share code, notes, and snippets.

@jneander
Last active August 29, 2015 14:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jneander/f58529c97d5236fcb321 to your computer and use it in GitHub Desktop.
Save jneander/f58529c97d5236fcb321 to your computer and use it in GitHub Desktop.
Setting up a Ubuntu instance to run NGINX and Unicorn, with deployment using Capistrano.

Disclaimer: This is a work in progress and may not represent best practices.

Linux Instance Setup

Update Linux packages.

sudo apt-get update

Security

Create the 'deploy' user

sudo adduser --disabled-password deploy

Enable public key authentication for the 'deploy' user.

sudo mkdir /home/deploy/.ssh
sudo chown -R deploy /home/deploy/.ssh
sudo chgrp -R deploy /home/deploy/.ssh
sudo chmod -R 700 /home/deploy/.ssh

Paste a public SSH key into /home/deploy/.ssh/authorized_keys. Then set read/write permissions.

sudo chmod 600 /home/deploy/.ssh/authorized_keys
sudo chown deploy /home/deploy/.ssh/authorized_keys
sudo chgrp deploy /home/deploy/.ssh/authorized_keys

Grant the 'deploy' user sudo access and remove its password requirement. With sudo, add the following line to /etc/sudoers.

deploy  ALL=(ALL) NOPASSWD: ALL

Disable password authentication and remote root access. The relevant settings might be set by default, depending on how your server was instantiated. Ensure the following settings are applied in /etc/ssh/sshd_config.

PermitRootLogin no
PasswordAuthentication no
X11Forwarding no
AllowUsers deploy # add any additional authorized users here (space-separated)

Use sudo service ssh reload to apply configuration changes.

Enable restrictive iptables.

sudo ufw logging on
sudo ufw allow ssh
sudo ufw allow www
sudo ufw enable

Install Ruby Environment

Install system dependencies.

sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev

Log in as the 'deploy' user (using sudo su - deploy).

Clone the rbenv, ruby-build, and rbenv-gem-rehash git repositories.

git clone git://github.com/sstephenson/rbenv.git .rbenv
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
git clone https://github.com/sstephenson/rbenv-gem-rehash.git ~/.rbenv/plugins/rbenv-gem-rehash

Ensure the following lines are at the start of the 'deploy' user's ~/.bashrc file.

export PATH="$HOME/.rbenv/bin:$PATH"
export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"
eval "$(rbenv init -)"

Reload the shell using exec $SHELL.

Install rbenv and ruby.

rbenv install 2.2.2 # set the desired ruby version here
rbenv global 2.2.2

Skip installing Rubygem documentation when installing gems. Then install Bundler.

echo "gem: --no-ri --no-rdoc" > ~/.gemrc
gem install bundler

Setup Deploy Configuration

SSH onto the instance as 'deploy'.

Ensure the path exists for the deployed application.

mkdir /home/deploy/<app-name>

Configure NGINX

Remove the default nginx site.

sudo rm /etc/nginx/sites-enabled/default

Link the application's nginx.conf to a new site in the nginx sites-enabled path.

sudo ln -nfs /home/deploy/<app-name>/current/config/nginx.conf /etc/nginx/sites-enabled/<app-name>

Restart nginx.

sudo service nginx restart

Configure Unicorn

Set the unicorn service to run at system startup.

cp init.sh /etc/init.d/unicorn_<app-name>
sudo chmod 755 /etc/init.d/unicorn_<app-name>
sudo update-rc.d unicorn_<app-name> defaults

Resources

Dependencies

set :application, 'testapp'
set :repo_url, 'git@github.com:<github-user>/<repo-name>.git'
set :deploy_to, '/home/deploy/<app-name>'
set :user, 'deploy'
set :linked_dirs, %w{log tmp/pids tmp/cache tmp/sockets}
namespace :deploy do
%w[start stop restart].each do |command|
desc 'Manage Unicorn'
task command do
on roles(:app), in: :sequence, wait: 1 do
execute "/etc/init.d/unicorn_#{fetch(:application)} #{command}"
end
end
end
after :publishing, :restart
end
role :app, %w{deploy@<server-ip-or-domain-name>}
role :web, %w{deploy@<server-ip-or-domain-name>}
role :db, %w{deploy@<server-ip-or-domain-name>}
#!/bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Manage unicorn server
# Description: Start, stop, restart unicorn server for a specific application.
### END INIT INFO
set -e
# Example init script, this can be used with nginx, too,
# since nginx and unicorn accept the same signals
# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/home/deploy/<app-name>/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb"
INIT_CONF=$APP_ROOT/config/init.conf
action="$1"
set -u
test -f "$INIT_CONF" && . $INIT_CONF
old_pid="$PID.oldbin"
cd $APP_ROOT || exit 1
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $old_pid && kill -$1 `cat $old_pid`
}
case $action in
start)
sig 0 && echo >&2 "Already running" && exit 0
$CMD
;;
stop)
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && exit 0
echo >&2 "Not running"
;;
restart|reload)
sig HUP && echo reloaded OK && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
upgrade)
if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
then
n=$TIMEOUT
while test -s $old_pid && test $n -ge 0
do
printf '.' && sleep 1 && n=$(( $n - 1 ))
done
echo
if test $n -lt 0 && test -s $old_pid
then
echo >&2 "$old_pid still exists after $TIMEOUT seconds"
exit 1
fi
exit 0
fi
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
$CMD
;;
reopen-logs)
sig USR1
;;
*)
echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
exit 1
;;
esac
root = "/home/deploy/<app-name>/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"
listen "/tmp/unicorn.testapp.sock"
worker_processes 1
timeout 30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment