Skip to content

Instantly share code, notes, and snippets.

@jeanfbrito
Created May 14, 2014 05:20
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 jeanfbrito/ca78979709c75de9e347 to your computer and use it in GitHub Desktop.
Save jeanfbrito/ca78979709c75de9e347 to your computer and use it in GitHub Desktop.
Setting up a Digital Ocean Droplet (VPS) on Ubuntu with Rails, Nginx, Unicorn, Postgres, Redis and Capistrano
http://www.andrewgertig.com/2013/11/setting-up-a-digital-ocean-droplet-vps-on-ubuntu-with-rails-nginx-unicorn-postgres-redis-and-capistrano
Setting up a Digital Ocean Droplet (VPS) on Ubuntu with Rails, Nginx, Unicorn, Postgres, Redis and Capistrano
November 16, 2013
Create a Droplet on Digital Ocean
I stuck with the defaults:
Size
512MB / 1 CPU
20GB SSD
$5 / Month
Image
Ubuntu 12.04 x32
ssh to root in terminal with your server's ip address
ssh root@123.123.123.123
Add ssh fingerprint and enter the password provided in the email from DO
Change password
passwd
Create new user. I like calling this guy "deployer" so that's what you'll see throughout this post.
adduser deployer
Set new users privileges
visudo
Find user privileges section and add your new user privileges under root. Type cntrl+x then y to save
# User privilege specification
root ALL=(ALL:ALL) ALL
deployer ALL=(ALL:ALL) ALL
Configure SSH
nano /etc/ssh/sshd_config
Find and change port to one that isn't default(22 is default: choose between 1025 ..65536)
# Port 22 # change this to whatever port you wish to use I'm using 1026 for this example
Port 1026
Protocol 2
PermitRootLogin yes # You can change this to "no" if you don't want the root user to be able to ssh in anymore
Add to bottom of sshd_config file after changing port (cntrl+x then y to save)
UseDNS no
AllowUsers deployer # Only add this line if you have changed PermitRootLogin to no, or you can do AllowUsers deployer root
Reload ssh
reload ssh
Don't close root! Open a new shell and ssh to your VPS (droplet) with deployer (remember the port you set above or you'll be locked out)
ssh -p 1026 deployer@123.123.123.123
Update the packages as deployer on your VPS. This installs curl, some python stuff, git and node.js (node.js is for the Rails asset pipeline)
sudo apt-get update
sudo apt-get -y install curl python-software-properties git-core nodejs
Install Redis (optional)
Prepare to install Redis
sudo apt-get -y install build-essential tcl8.5
Change to your home directory and download Redis with wget (I use it for Sidekiq)
cd ~
wget http://redis.googlecode.com/files/redis-2.4.16.tar.gz
Untar Redis, switch into its directory then run make and make install. Then run the Redis install script.
tar xzf redis-2.4.16.tar.gz
cd redis-2.4.16
make
make test
sudo make install
cd utils
sudo ./install_server.sh
To automatically start Redis when the server boots
sudo update-rc.d redis_6379 defaults
RVM
Install latest stable version of rvm
curl -L get.rvm.io | bash -s stable
Load rvm
source ~/.rvm/scripts/rvm
Install rvm dependencies
rvm requirements
Install ruby 2.0.0
rvm install 2.0.0
Use 2.0.0 as rvm default
rvm use 2.0.0 --default
Install latest version of rubygems if rvm install didn't
rvm rubygems current
Rails
Install the rails gem
gem install rails --no-ri --no-rdoc
Install bundler
gem install bundler
PostgreSQL
Install postgres
sudo apt-get install postgresql postgresql-server-dev-9.1
gem install pg -- --with-pg-config=/usr/bin/pg_config
Create a new postgres user. It's not a bad idea to use the same username here. Remember these so you can add them to your database.yml file later.
sudo -u postgres psql
create user deployer with password 'secret';
alter role deployer superuser createrole createdb replication;
create database projectname_production owner deployer;
\q
Nginx
Setup nginx
sudo apt-get install nginx
These 3 commands test if nginx is installed
nginx -h
cat /etc/init.d/nginx
/etc/init.d/nginx -h
Start Nginx
sudo service nginx start
# cd /etc/nginx
Preparing for Deployment locally with Unicorn, Nginx and Capistrano
Add unicorn to the gemfile
gem "unicorn"
Manually create these 3 files:
config/nginx.conf
config/unicorn.rb
config/unicorn_init.sh
config/nginx.conf (change projectname and username, I'm using deployer, to match your directory structure!)
upstream unicorn {
server unix:/tmp/unicorn.projectname.sock fail_timeout=0;
}
server {
listen 80 default deferred;
# server_name example.com;
root /home/deployer/apps/projectname/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri @unicorn;
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
config/unicorn.rb
root = "/home/deployer/apps/projectname/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.projectname.sock"
worker_processes 2
timeout 30
# Force the bundler gemfile environment variable to
# reference the capistrano "current" symlink
before_exec do |_|
ENV["BUNDLE_GEMFILE"] = File.join(root, 'Gemfile')
end
config/unicorn_init.sh
#!/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
# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/home/deployer/apps/projectname/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="cd $APP_ROOT; bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
AS_USER=deployer
set -u
OLD_PIN="$PID.oldbin"
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
}
run () {
if [ "$(id -un)" = "$AS_USER" ]; then
eval $1
else
su -c "$1" - $AS_USER
fi
}
case "$1" in
start)
sig 0 && echo >&2 "Already running" && exit 0
run "$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"
run "$CMD"
;;
upgrade)
if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
then
n=$TIMEOUT
while test -s $OLD_PIN && test $n -ge 0
do
printf '.' && sleep 1 && n=$(( $n - 1 ))
done
echo
if test $n -lt 0 && test -s $OLD_PIN
then
echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
exit 1
fi
exit 0
fi
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
run "$CMD"
;;
reopen-logs)
sig USR1
;;
*)
echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
exit 1
;;
esac
Change permissions on unicornt_init.sh
chmod +x config/unicorn_init.sh
Capistrano
Add capistrano and rvm capistrano to gemfile
gem 'capistrano'
gem 'rvm-capistrano'
Create Capfile & config/deploy.rb files by running
capify .
For deploy.rb you should change the server ip address, :user (I'm using deployer), :repository path (gertig), and note that :port is not 22 since we changed it above to 1026
config/deploy.rb
require "bundler/capistrano"
require "rvm/capistrano"
require 'sidekiq/capistrano'
server "123.123.123.123", :web, :app, :db, primary: true
set :application, "projectname"
set :user, "deployer"
set :port, 1026
set :deploy_to, "/home/#{user}/apps/#{application}"
set :deploy_via, :remote_cache
set :use_sudo, false
set :scm, "git"
set :repository, "git@github.com:gertig/reponame.git"
set :branch, "master"
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
after "deploy", "deploy:cleanup" # keep only the last 5 releases
namespace :deploy do
%w[start stop restart].each do |command|
desc "#{command} unicorn server"
task command, roles: :app, except: {no_release: true} do
run "/etc/init.d/unicorn_#{application} #{command}"
end
end
task :setup_config, roles: :app do
sudo "ln -nfs #{current_path}/config/nginx.conf /etc/nginx/sites-enabled/#{application}"
sudo "ln -nfs #{current_path}/config/unicorn_init.sh /etc/init.d/unicorn_#{application}"
run "mkdir -p #{shared_path}/config"
put File.read("config/database.example.yml"), "#{shared_path}/config/database.yml"
puts "Now edit #{shared_path}/config/database.yml and add your username and password"
end
after "deploy:setup", "deploy:setup_config"
task :symlink_config, roles: :app do
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
end
after "deploy:finalize_update", "deploy:symlink_config"
desc "Make sure local git is in sync with remote."
task :check_revision, roles: :web 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
before "deploy", "deploy:check_revision"
end
Capfile
load 'deploy'
load 'deploy/assets'
load 'config/deploy'
SSH in to Github to add your keys
# follow the steps in this guide if receive permission denied(public key)
# https://help.github.com/articles/error-permission-denied-publickey
ssh github@github.com
Add ssh key to digitalocean
cat ~/.ssh/id_rsa.pub | ssh -p 1026 deployer@123.123.123.123 'cat >> ~/.ssh/authorized_keys'
Add your Environment variables like ENV["SECRET_KEY"]
sudo nano /etc/environment
On each new line add something like
SECRET_TOKEN=55555DDDDDDDEEEE44444
REDIS_URL=redis://123.123.123.123:6379
Create repo and push to github
# Add config/database.yml to .gitignore
cp config/database.yml config/database.example.yml
git init
git add .
git commit -m "Inital Commit"
git remote add origin git@github.com:gertig/reponame
git push origin master
DEPLOYMENT
cap deploy:setup
Edit database.yml on server to add username and password to the production database
nano apps/projectname/shared/config/database.yml
Deploy it cold
cap deploy:cold
Then remove the nginx default folder (if it doesn't exist don't worry about it)
sudo rm /etc/nginx/sites-enabled/default
Restart Nginx
sudo service nginx restart
sudo update-rc.d -f unicorn_projectname defaults
Now whenever you make changes do the following.
# Make changes
git add .
git commit -m "Changes"
git push origin master
Deploy
cap deploy
Stop and Start the unicorn server
sudo service unicorn_appname stop
sudo service unicorn_appname start
Restart Nginx
sudo service nginx restart
Troubleshooting
Nokogiri
If Nokogiri complains about libxml run this:
sudo apt-get install libxslt-dev libxml2-dev
Then try your cap deploy:cold again if that is where you failed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment