SSH into Root
$ ssh [email protected]
Change Root Password
$ passwd
Add Deploy User
$ adduser deployer
Update Sudo Privileges
$ visudo
username ALL=(ALL:ALL) ALL
Configure SSH
$ vi /etc/ssh/sshd_config
Port 22 # Change (1025..65536)
Protocol 2 # Change
PermitRootLogin no # Change
UseDNS no # Add
AllowUsers deployer # Add
Reload SSH
$ reload ssh
Make sure your system is up to date sudo apt-get update && apt-get upgrade
SSH with Deploy User (Don't close root)
$ ssh -p 1026 [email protected]
Install Curl
$ sudo apt-get update
$ sudo apt-get install curl
Install RVM
sudo apt-get install libgdbm-dev libncurses5-dev automake libtool bison libffi-dev
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm install 2.3.1
rvm use 2.3.1 --default
ruby -v
We'll be using that to setup our production server because it's very easy to setup.
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update
sudo apt-get install -y nginx-extras passenger
sudo apt-get install nginx
Install PostgreSQL
$ sudo apt-get install postgresql postgresql-server-dev-9.3
$ gem install pg -- --with-pg-config=/usr/bin/pg_config
Create Postgres User
$ sudo -u postgres psql
create user deployer with password 'password';
alter role deployer superuser createrole createdb replication;
create database MYAPP_production owner deployer;
Install GIT
$ sudo apt-get install git-core
Install Bundler
$ gem install bundler
Setup Nginx
$ sudo apt-get install nginx
$ nginx -h
$ cat /etc/init.d/nginx
$ /etc/init.d/nginx -h
$ sudo service nginx start
$ sudo vim /etc/nginx/sites-enabled/default
upstream unicorn {
server unix:/tmp/unicorn.MYAPP.sock fail_timeout=0;
}
server {
listen 80 default deferred;
# server_name example.com;
root /home/deployer/apps/MYAPP/current/public;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
location ~ ^/(robots.txt|sitemap.xml.gz)/ {
root /home/deployer/apps/MYAPP/current/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;
}
Add Unicorn
$ vim Gemfile
gem 'unicorn'
Update Unicorn Config
$ vim config/unicorn/production.rb
root = "/home/deployer/apps/MYAPP/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.log"
stdout_path "#{root}/log/unicorn.log"
worker_processes Integer(ENV['WEB_CONCURRENCY'])
timeout 30
preload_app true
listen '/tmp/unicorn.spui.sock', backlog: 64
before_fork do |server, worker|
Signal.trap 'TERM' do
puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
Process.kill 'QUIT', Process.pid
end
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
end
after_fork do |server, worker|
Signal.trap 'TERM' do
puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
end
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
end
# Force the bundler gemfile environment variable to
# reference the capistrano "current" symlink
before_exec do |_|
ENV['BUNDLE_GEMFILE'] = File.join(root, 'Gemfile')
end
Add Capistrano
$ vim Gemfile
group :development do
gem 'capistrano-rails'
gem 'capistrano-rvm'
gem 'capistrano3-unicorn'
end
Install Capistrano
$ bundle exec cap install
Update Capistrano Capfile
$ vim Capfile
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano3/unicorn'
# Load custom tasks from `lib/capistrano/tasks' if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
Update Capistrano Deploy Config
$ vim config/deploy.rb
lock '3.3.5'
set :application, 'spui'
set :repo_url, '[email protected]:MYGITHUB/MYAPP.git'
ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
set :use_sudo, false
set :bundle_binstubs, nil
set :linked_files, fetch(:linked_files, []).push('config/database.yml')
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
task :restart do
invoke 'unicorn:reload'
end
end
Update Production Deploy Config
$ vim config/deploy/production.rb
set :port, 22
set :user, 'deployer'
set :deploy_via, :remote_cache
set :use_sudo, false
server '123.333.333.333',
roles: [:web, :app, :db],
port: fetch(:port),
user: fetch(:user),
primary: true
set :deploy_to, "/home/#{fetch(:user)}/apps/#{fetch(:application)}"
set :ssh_options, {
forward_agent: true,
auth_methods: %w(publickey),
user: 'deployer',
}
set :rails_env, :production
set :conditionally_migrate, true
Add SSH Key to DigitalOcean
$ cat ~/.ssh/id_rsa.pub | ssh -p 22 [email protected] 'cat >> ~/.ssh/authorized_keys'
Say Hi to Github
# follow the steps in this guide if receive permission denied(public key)
# https://help.github.com/articles/error-permission-denied-publickey
$ ssh [email protected]
Check Deployment (Commit and Push)
$ cap production deploy:check
Deploy
$ cap production deploy
The nginx install process on ubuntu creates a folder at /etc/nginx. In this folder you’ll find a few configuration files, along with a sites-available and a sites-enabled folder. The sites-available folder is where you’ll configure your application-specific nginx setup, and /etc/nginx/nginx.conf is most likely where you’ll configure global nginx settings. The following is a sensibly configured nginx.conf.
user www-data;
worker_processes 1;
pid /var/run/nginx.pid;
events { worker_connections 1024; }
http {
sendfile on; tcp_nopush on; tcp_nodelay off; keepalive_timeout 65; types_hash_max_size 2048; server_tokens off;
include /etc/nginx/mime.types; default_type application/octet-stream;
access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log;
gzip on; gzip_disable "msie6"; gzip_http_version 1.1; gzip_proxied any; gzip_min_length 500; gzip_types text/plain text/xml text/css text/comma-separated-values text/javascript application/x-javascript application/atom+xml;
upstream unicorn { server unix:/app/path/tmp/unicorn-staging.sock fail_timeout=0; }
include /etc/nginx/conf.d/.conf; include /etc/nginx/sites-enabled/; }
server { listen 80; server_name mysite.com; # Replace this with your site's domain.
keepalive_timeout 300;
client_max_body_size 4G;
root /app/root/public; # Set this to the public folder location of your Rails application.
try_files $uri/index.html $uri.html $uri @unicorn;
location @unicorn { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Forwarded_Proto $scheme; proxy_redirect off; # This passes requests to unicorn, as defined in /etc/nginx/nginx.conf proxy_pass http://unicorn; proxy_read_timeout 300s; proxy_send_timeout 300s; }
error_page 500 502 503 504 /500.html; location = /500.html { root /app/root/public; } }
user capbeast;
worker_processes 4;
worker_rlimit_nofile 100000;
events {
worker_connections 2048;
# optmized to serve many clients with each thread, essential for linux
use epoll;
# accept as many connections as possible, may flood worker connections if set too low
multi_accept on;
}
http {
# Hide nginx version information.
server_tokens off;
# Define the MIME types for files.
include mime.types;
default_type application/octet-stream;
# Update charset_types due to updated mime.types
charset_types text/xml text/plain text/vnd.wap.wml application/x-javascript application/rss+xml text/css application/javascript application/json;
# Format to use in log files
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Speed up file transfers by using sendfile() to copy directly
# between descriptors rather than using read()/write().
sendfile on;
# How long to allow each connection to stay idle; longer values are better
# for each individual client, particularly for SSL, but means that worker
# connections are tied up longer. (Default: 65)
keepalive_timeout 65;
# Tell Nginx not to send out partial frames; this increases throughput
# since TCP frames are filled up before being sent out. (adds TCP_CORK)
tcp_nopush on;
gzip on;
gzip_min_length 100;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
gzip_buffers 128 128k;
gzip_types
application/atom+xml
application/javascript
application/json
application/rss+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/svg+xml
image/x-icon
text/css
text/plain
text/x-component;
server_names_hash_bucket_size 64;
passenger_root /usr/local/rvm/gems/ruby-2.1.2/gems/passenger-4.0.45;
passenger_ruby /usr/local/rvm/gems/ruby-2.1.2/wrappers/ruby;
passenger_max_pool_size 12;
passenger_min_instances 12;
#dev.capbeast.com block
server {
listen 80;
server_name dev.capbeast.com;
root /home/capbeast/capbeast_development/current/public;
# auth_basic "Restricted";
# auth_basic_user_file /home/capbeast/capbeast/.htpasswd;
client_body_in_file_only clean;
client_body_buffer_size 128K;
client_max_body_size 1G;
send_timeout 1000s;
passenger_enabled on;
rails_env production;
}
server {
server_name www.capbeast.com;
rewrite ^/(.*) https://capbeast.com/$1 permanent;
}
# listen 80;
# server_name localhost;
# access_log off;
# allow 127.0.0.1;
# deny all;
# location /nginx_status {
# stub_status on;
# }
# }
server {
listen 80;
server_name capbeast.com;
return 301 https://$server_name$request_uri;
}
# HTTPS server
server {
listen 443 ssl;
server_name capbeast.com designer.capbeast.com;
root /home/capbeast/capbeast/current/public;
# include h5bp/basic.conf;
client_body_in_file_only clean;
client_body_buffer_size 128K;
client_max_body_size 1G;
send_timeout 1000s;
# location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
# expires 3d;
# }
pagespeed on;
pagespeed FileCachePath /var/ngx_pagespeed_cache; # Use tmpfs for best results.
location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" { add_header "" ""; }
location ~ "^/ngx_pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }
location /ngx_pagespeed_statistics { allow 127.0.0.1; deny all;}
location /ngx_pagespeed_global_statistics { allow 127.0.0.1; deny all;}
location /ngx_pagespeed_message { allow 127.0.0.1; deny all;}
location /pagespeed_console { allow 127.0.0.1; deny all;}
if ($host !~* ^(capbeast.com|www.capbeast.com|designer.capbeast.com)$ ) {
return 405;
}
ssl on;
ssl_certificate /etc/ssl/certs/capbeast_ssl_certs/bundle.crt;
ssl_certificate_key /etc/ssl/certs/capbeast_ssl_certs/capbeast.key;
passenger_enabled on;
rails_env production;
}
}