Skip to content

Instantly share code, notes, and snippets.

@yosukehasumi
Last active October 22, 2021 15:12
Show Gist options
  • Star 46 You must be signed in to star a gist
  • Fork 19 You must be signed in to fork a gist
  • Save yosukehasumi/ea882abdcdf6f17d0d12280de136dce5 to your computer and use it in GitHub Desktop.
Save yosukehasumi/ea882abdcdf6f17d0d12280de136dce5 to your computer and use it in GitHub Desktop.
DigitalOcean Rails/Ubuntu/NGINX (16.04) Setup

DigitalOcean Rails/Ubuntu/NGINX (16.04) Setup

  1. Setup
  2. Swapfile
  3. NGINX
  4. ElasticSearch
  5. RVM
  6. Rails
  7. Postgres
  8. Capistrano
  9. Let's Encrypt
  10. Firewall

Droplet

Create a new, start with the smallest droplet. Use Ubuntu 16.04, log in as root.

1. Create non-root user 'rails'

Initial Server Setup with Ubuntu 16.04

create user 'rails'

adduser rails

Add to sudoers group

usermod -aG sudo rails

Switch user to 'rails'

su - rails

Create ssh key

ssh-keygen

Create sym link for webroot

cd ~
ln -s /var/www/html html

2. Swapfile

How To Add Swap Space on Ubuntu 16.04

Check swapfile

free -h

Check freespace

df -h

Create a 4gb Swap File

sudo fallocate -l 4G /swapfile

Verify

ls -lh /swapfile

Lock it down

sudo chmod 600 /swapfile

Make it swap

sudo mkswap /swapfile

Enable it

sudo swapon /swapfile

Verify

sudo swapon --show

Check swapfile

free -h

Make it permanent

sudo cp /etc/fstab /etc/fstab.bak;
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab

3. NGINX

How To Install Nginx on Ubuntu 16.04

Install NGINX

sudo apt-get update
sudo apt-get install nginx

Check status

systemctl status nginx

Tweak UFW firewall

sudo ufw allow 'OpenSSH'
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status

Install CURL, get your IP, and check that you can request a page in your browser!

sudo apt-get install curl
curl -4 icanhazip.com

Actions*

sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx

Server Configuration

/etc/nginx - The nginx configuration directory. All of the Nginx configuration files reside here.

/etc/nginx/nginx.conf The main Nginx configuration file. This can be modified to make changes to the Nginx global configuration.

/etc/nginx/sites-available/ The directory where per-site "server blocks" can be stored. Nginx will not use the configuration files found in this directory unless they are linked to the sites-enabled directory (see below). Typically, all server block configuration is done in this directory, and then enabled by linking to the other directory.

/etc/nginx/sites-enabled/ The directory where enabled per-site "server blocks" are stored. Typically, these are created by linking to configuration files found in the sites-available directory.

/etc/nginx/snippets This directory contains configuration fragments that can be included elsewhere in the Nginx configuration. Potentially repeatable configuration segments are good candidates for refactoring into snippets.

Server Logs

/var/log/nginx/access.log Every request to your web server is recorded in this log file unless Nginx is configured to do otherwise.

/var/log/nginx/error.log Any Nginx errors will be recorded in this log.

4. ElasticSearch

How To Install and Configure Elasticsearch on Ubuntu 16.04 Elastic search requires at least 2GB of ram to work for some reason.

Install JAVA

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get install oracle-java8-installer

Get ElasticSearch

sudo apt-get update
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.4.0.deb

Install

sudo dpkg -i elasticsearch-5.4.0.deb

Remove DEB file

rm elasticsearch-5.4.0.deb

Enable

sudo systemctl enable elasticsearch.service

Configure

sudo nano /etc/elasticsearch/elasticsearch.yml

Edit Config File

...
cluster.name: mycluster1
node.name: "My First Node"
...

Restart

sudo systemctl start elasticsearch.service

Tweak UFW firewall

sudo ufw allow from 127.0.0.1 to any port 9200
sudo ufw status

Test

curl -X GET 'http://localhost:9200'

Status

sudo systemctl status elasticsearch.service

5. RVM

How To Install Ruby on Rails with RVM on Ubuntu 16.04

Add key

gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3

Move to a writeable directory

cd /tmp

Get latest Ruby

curl -sSL https://get.rvm.io -o rvm.sh

Compile

cat /tmp/rvm.sh | bash -s stable --rails

Reload your source file

source /etc/profile.d/rvm.sh

Install your Ruby VERSION

rvm install VERSION --default

6. Rails

Deploying a Rails App on Ubuntu 14.04 with Capistrano, Nginx, and Puma

Install Rails and bundler

gem install rails -v '5.0.1' -V --no-ri --no-rdoc
gem install bundler -V --no-ri --no-rdoc

Authorize your server to read from your repo, you'll have to add a deployment key to your repo. To do this you must copy your ~/.ssh/id_rsa.pub file to your repo (make sure this is your deployment user, eg. rails)

ssh -T git@github.com
ssh -T git@bitbucket.org
ssh -T git@gitlab.com

Change Permissions on html directory

sudo chown rails:rails /var/www/html/

Clone repo

git clone git@gitlab.com:gituser/example.git

Add your local key to the server, if this doesn't work just add your local public key (id_rsa.pub) to /home/rails/.ssh/authorized_keys

ssh-copy-id rails@example.com

7. Postgres

How To Install and Use PostgreSQL on Ubuntu 16.04

Install Postgres

sudo apt-get update
sudo apt-get install postgresql postgresql-contrib

Create 'rails' postgres user

sudo -i -u postgres
createuser --interactive

Make it look like this:

Enter name of role to add: rails
Shall the new role be a superuser? (y/n) y

Create the database for rails user

createdb rails

libpq has to be installed separately

sudo apt-get install libpq-dev

8. Capistrano

Add gems to deployment

group :development do
  gem 'capistrano'
  gem 'capistrano-rails'
  gem 'capistrano-rvm'
  gem 'capistrano3-puma', github: 'seuros/capistrano-puma'
  gem 'capistrano-bundler'
  gem 'capistrano-rails-console'
end

Install Capistrano

cap install

Capfile

require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rails'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/puma'
require 'capistrano/scm/git'
require 'capistrano/rails/console'

set :rvm_type, :user
set :rvm_ruby_version, '2.3.1'
install_plugin Capistrano::Puma
install_plugin Capistrano::SCM::Git
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

Deploy.rb, replace (APP_NAME)

lock "3.8.1"

set :application, "APP_NAME"
set :repo_url, "git@gitlab.com:gituser/example.git"
set :deploy_to, "/home/rails/html"
set :pty, true
set :puma_conf, "#{shared_path}/config/puma.rb"

append :linked_files, "config/database.yml", "config/secrets.yml", ".env"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"

Production.rb, replace SERVER_IP

server 'SERVER_IP', user: 'rails', roles: %w{app db web}

Create shared config files

mkdir -p /var/www/html/shared/config
touch /var/www/html/shared/config/database.yml
touch /var/www/html/shared/config/secrets.yml
touch /var/www/html/shared/config/puma.rb
touch  /home/rails/html/shared/.env

Copy config in databse.yml

production:
  adapter: postgresql
  encoding: unicode
  pool: 5
  database: APP_PRODUCTION
  username: rails
  url: localhost
  password: <%= ENV['DATABASE_PASSWORD'] %>

Copy config into secrets.yml

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

Copy config into .env

SECRET_KEY_BASE=mysupersecretkeybase
DATABASE_URL=127.0.0.1
DATABASE_PASSWORD=mysupersecretdatabasepassword

Copy config in puma.rb

#!/usr/bin/env puma

directory '/home/rails/html/current'
rackup "/home/rails/html/current/config.ru"
environment 'production'

tag ''

pidfile "/home/rails/html/shared/tmp/pids/puma.pid"
state_path "/home/rails/html/shared/tmp/pids/puma.state"
stdout_redirect '/home/rails/html/shared/log/puma_access.log', '/home/rails/html/shared/log/puma_error.log', true

threads 0,16

bind 'unix:///home/rails/html/shared/tmp/sockets/puma.sock'

workers 0

prune_bundler

on_restart do
  puts 'Refreshing Gemfile'
  ENV["BUNDLE_GEMFILE"] = "/home/rails/html/current/Gemfile"
end


May need Node.js for Javascript Runtime

sudo apt-get install nodejs

9 Let's Encrypt

How To Secure Nginx with Let's Encrypt on Ubuntu 16.04

Install Certbot

sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot

Edit NGINX

sudo nano /etc/nginx/sites-available/default

Inside the server block, add this location block

  location ~ /.well-known {
    allow all;
  }

Check your NGINX config file

sudo nginx -t

Restart NGINX

sudo systemctl restart nginx

Get your cert (make sure you know your webroot)

sudo certbot certonly --webroot --webroot-path=/var/www/html -d example.com -d www.example.com

Generate strong diff

sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

Create configuration snippet

sudo nano /etc/nginx/snippets/ssl-example.com.conf

Paste in your certificates

ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

Create strong encryption params

sudo nano /etc/nginx/snippets/ssl-params.conf

Paste in params

# from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# disable HSTS header for now
#add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

ssl_dhparam /etc/ssl/certs/dhparam.pem;

Nginx Server finalization

How To Secure Nginx with Let's Encrypt on Ubuntu 16.04

create a new Nginx config file

touch /etc/nginx/sites-available/rails
chmod 644 rails

Update your APPLICATIONSOCKET.sock, server_name, snippet location

upstream app_server {
    server unix:///home/rails/html/shared/tmp/sockets/APPLICATIONSOCKET.sock fail_timeout=0;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name EXAMPLE.COM;
    return 301 https://$server_name$request_uri;
}

server {
    # SSL configuration
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;

    include snippets/ssl-example.com.conf;
    include snippets/ssl-params.conf;

    root /home/rails/html/current/public;

    index index.htm index.html;

    location / {
        try_files $uri/index.html $uri.html $uri @app;
    }

    location ~* ^.+\.(jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|mp3|flv|mpeg|avi)$ {
        try_files $uri @app;
    }

    location @app {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://app_server;
    }

    location ~ /.well-known {
        allow all;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

Update symobolic link

cd /etc/nginx/sites-enabled
rm default
ln -s /etc/nginx/sites-available/rails default

Restart NGINX

sudo systemctl restart nginx
@FelixFortis
Copy link

Thanks for the useful guide! When you clone the repo initially, are you cloning it into html -> /var/www/html ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment