using Unicorn, Nginx, PostgreSQL, active_storage, Amazon S3. Combined summary of all guides.
Your root
password is e-mailed to you.
Create SSH Key [Guide]
Enter .ssh
directory:
$ cd ~/.ssh
Generate Key:
$ ssh-keygen
Save DigitalOcean (DO
) key with created key-password
$ ~/.ssh/id_do_mac
Save key-password
in password manager
Add Public Key To Digital Ocean [Guide]
Copy public key to clip board:
$ cat ~/.ssh/id_do_mac.pub | pbcopy
Add SSH Key to Digital Ocean Account Name MacOS
:
Home>Security>Add SSH Key
π» 2. Initial Server Setup [Guide]
$ ssh root@your_server_ip
$ adduser deploy
Create deploy-password
add to password manager.
Give deploy
root privileges:
$ usermod -aG sudo deploy
Use already created local SSH key and add to the server.
## Local Machine
$ cat ~/.ssh/id_do_mac.pub | pbcopy
Paste SSH Key to Deployed User's Authorized Keys
## Server
$ su - deploy
$ mkdir ~/.ssh
$ chmod 700 ~/.ssh
$ nano ~/.ssh/authorized_keys
# Paste Key
Change permissions back:
$ chmod 600 ~/.ssh/authorized_keys
$ sudo nano /etc/ssh/sshd_config
Find: PasswordAuthentication
change to
PasswordAuthentication no
Reload: sudo systemctl reload sshd
Test Login: ssh deploy@server_ip
$ sudo ufw allow OpenSSH
$ sudo ufw enable
# Enter: y
$ sudo ufw status
# Status: active
$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp
π 3. Install Ruby on Rails & rbenv [Guide]
Login as deploy
(i.e. ssh deploy@server_ip
or su - deploy
)
Install nodejs
dependence for Asset Pipeline
$ curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.sh
$ sudo bash nodesource_setup.sh
$ sudo apt-get install nodejs
$ nodejs -v
# Output: v8.10.0
Ruby + rbenv
dependencies`
$ sudo apt-get update
$ sudo apt-get install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm3 libgdbm-dev
Add rbenv
& ruby-build
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ source ~/.bashrc
$ type rbenv
# output: ...
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
Install ruby 2.5.0
& gems
$ rbenv install 2.5.0
$ rbenv global 2.5.0
$ echo "gem: --no-document" > ~/.gemrc
# No Docs
$ gem install bundler
$ gem install rails
# rails -v: Rails 5.2
Add dependencies:
$ sudo apt-get install postgresql postgresql-contrib libpq-dev
Create user (this will be same user in your database.yml
, username
field for the production
tag):
$ sudo -u postgres createuser -s <APPNAME>
Set password for APPNAME
$ sudo -u postgres psql
$ \password <APPNAME>
Save Postgres User and Password in password manager
(for Database Keys, S3 and Secrets)
ON SERVER:
Install rbenv-vars
for environment variable management.
$ cd ~/.rbenv/plugins
$ git clone https://github.com/sstephenson/rbenv-vars.git
ON LOCAL TERMINAL/PROJECT:
Generate config/master.key
for Rails Encrypted Credentials by editing credentials
bin/rails credentials:edit
# generates credentials.yml.enc
Add the postgres production database password to credentials.yml.enc
.
## credentials.ymc.enc
database:
production_password: <PASSWORD-CREATED-IN-STEP-3-ABOVE>
Edit database.yml
## database.yml
production:
adapter: postgresql
encoding: unicode
database: <APPNAME_production>
host: localhost
pool: 5
username: <APPNAME-CREATED-IN-STEP-3-ABOVE>
password: <%= Rails.application.credentials.dig(:database, :production_password) %>
## ...
Copy the RAILS_MASTER_KEY
from config/master.key
. Save RAILS_MASTER_KEY in Password Manager
$ cat config/master.key
# cfda33e2583...
ON SERVER:
Edit rbenv-vars
to add the RAILS_MASTER_KEY
(because Encrypted Credentials looks for ENV['RAILS_MASTER_KEY']
)
$ nano .rbenv-vars
## .rbenv-vars
RAILS_MASTER_KEY=<MASTER-KEY-HERE>
-
Create an S3 Access Key - Save ACCESS_KEY_ID and SECRET_ACCESS_KEY in Password Manager
ON LOCAL TERMINAL/PROJECT:
Add S3 Keys to Encrypted Credentials
bin/rails credentials:edit
# credentials.yml.enc
aws:
access_key_id: <s3_key_id>
secret_access_key: <s3_secret>
Edit storage.yml
## storage.yml
amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
region: us-east-1
bucket: <%= Rails.application.credentials.dig(:aws, :bucket) %>
## ...
Add, Commit & Push Changes to Remote Repo
$ git add -A
$ git commit -m "Add and encrypt deploy credentials"
$ git push origin master
ON SERVER:
Pull Git Repo with Rails app:
$ git clone https://github.com/username/repo
$ bundle install
# Installed
$ RAILS_ENV=production rake db:create
$ RAILS_ENV=production rake db:migrate
$ RAILS_ENV=production rake assets:precompile
$ RAILS_ENV=production rails server --binding=server_public_IP
# test http://server_ip:3000
If test fails allow firewall to port 3000 temporarily1:
$ sudo ufw allow 3000
π¦ 6. Installing Unicorn [Guide] OR [Passenger Phusion Guide]
Add gem 'unicorn'
to Gemfile
and bundle
.
Edit config/unicorn.rb
paste Appendix B: config/unicorn.rb
Save and add logging to Rails App:
$ mkdir -p shared/pids shared/sockets shared/log
Edit and add unicorn init script from Appendix C: Unicorn Init Script:
$ sudo nano /etc/init.d/unicorn_APPNAME
Ensure You Change APPNAME in Init Script to actual app
Update permissions for init script:
$ sudo chmod 755 /etc/init.d/unicorn_appname
$ sudo update-rc.d unicorn_appname defaults
Run via:
sudo service unicorn_appname start
If run fails and systemctl
reveals little, ensure that you rbenv
's installation added the RBENV_ROOT
properly. Otherwise edit your deploy
~/.profile
file and add the following and resart the service.
$ export RBENV_ROOT=/home/YOUR_USER_PATH/.rbenv
$ export PATH=$RBENV_ROOT/shims:$RBENV_ROOT/bin:$PATH
π‘ 7. Installing NGINX Reverse Proxy [Guide]
$ sudo apt-get install nginx
Add NGINX reverse proxy config with block in Appendix D: NGINX Reverse Proxy
$ sudo vi /etc/nginx/sites-available/default
Ensure You Change APPNAME in NGINX to actual app
Restart NGINX
$ sudo service nginx restart
# try: http://server_public_IP
Make sure you have all passwords: Appendix A: Inside Password Manager
All System Operational.
Digital_Ocean:
root_pass:
deploy_pass:
Postgres:
appname:
appname_password:
Rails_Credentials
RAILS_MASTER_KEY:
AWS:
access_key_id:
secret_access_key:
# Configures Unicorn with the location of your application, and the location of its socket, logs, and PIDs.
# set path to application
app_dir = File.expand_path("../..", __FILE__)
shared_dir = "#{app_dir}/shared"
working_directory app_dir
# Set unicorn options
worker_processes 2
preload_app true
timeout 30
# Set up socket location
listen "#{shared_dir}/sockets/unicorn.sock", :backlog => 64
# Logging
stderr_path "#{shared_dir}/log/unicorn.stderr.log"
stdout_path "#{shared_dir}/log/unicorn.stdout.log"
# Set master PID location
pid "#{shared_dir}/pids/unicorn.pid"
Replace APPNAME
#!/bin/sh
### BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the unicorn app server
# Description: starts unicorn using start-stop-daemon
### END INIT INFO
set -e
USAGE="Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
# app settings
USER="deploy"
APP_NAME="appname"
APP_ROOT="/home/$USER/$APP_NAME"
ENV="production"
# environment settings
PATH="/home/$USER/.rbenv/shims:/home/$USER/.rbenv/bin:$PATH"
CMD="cd $APP_ROOT && bundle exec unicorn -c config/unicorn.rb -E $ENV -D"
PID="$APP_ROOT/shared/pids/unicorn.pid"
OLD_PID="$PID.oldbin"
# make sure the app exists
cd $APP_ROOT || exit 1
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $OLD_PID && kill -$1 `cat $OLD_PID`
}
case $1 in
start)
sig 0 && echo >&2 "Already running" && exit 0
echo "Starting $APP_NAME"
su - $USER -c "$CMD"
;;
stop)
echo "Stopping $APP_NAME"
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
echo "Force stopping $APP_NAME"
sig TERM && exit 0
echo >&2 "Not running"
;;
restart|reload|upgrade)
sig USR2 && echo "reloaded $APP_NAME" && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
rotate)
sig USR1 && echo rotated logs OK && exit 0
echo >&2 "Couldn't rotate logs" && exit 1
;;
*)
echo >&2 $USAGE
exit 1
;;
esac
Replace APPNAME
upstream app {
# Path to Unicorn SOCK file, as defined previously
server unix:/home/deploy/<APPNAME>/shared/sockets/unicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name localhost;
root /home/deploy/<APPNAME>/public;
try_files $uri/index.html $uri @app;
location @app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}