Created
May 14, 2014 05:20
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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