Assumptions
Your app is called example .
Your domain name is example.com .
Disable SSH password connection
Edit /etc/ssh/sshd_config dans set:
PasswordAuthentication no
Make sure your public key has already been added by your provider:
cat .ssh/authorized_keys
Then restart SSH:
systemctl restart ssh
Create a deploy user
Create the user :
adduser deploy
Use a generated password and store it somewhere.
Then, create SSH keys for the created user (no passphrase):
su deploy
cd ~
ssh-keygen -t rsa
Upload the public key to BitBucket/Github:
cat .ssh/id_rsa.pub
exit
Setup public key authentication for deploy too.
As root:
cd ~
cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
chown deploy:deploy /home/deploy/.ssh/authorized_keys
You should be able the SSH without password for root
and deploy
now.
Base setup
As root:
apt-get update
apt-get upgrade
apt-get install zlib1g zlib1g-dev build-essential git-core curl emacs imagemagick nginx ntp
apt-get install mysql-client libmysqlclient-dev libopenssl-ruby1.9.1 libssl-dev libreadline-dev
apt-get install mysql-server monit unattended-upgrades logrotate memcached nodejs
apt-get install libcurl4-gnutls-dev libxml2 libxml2-dev libxslt1-dev ruby-dev
apt-get install mysql-client libmysqlclient-dev libssl-dev libreadline-dev screen
apt-get install libmagickcore-dev libmagickwand-dev graphicsmagick-libmagick-dev-compat
Set a generated password for mysql root and save it somewhere.
Enable automatic security updates
As root:
unattended-upgrades
Setup firewall
As root:
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 2812 -j ACCEPT
iptables -A INPUT -i lo -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 8 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 0 -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD DROP
iptables -L -n -v
Make rules persistent:
apt-get install iptables-persistent
service iptables-persistent save
service iptables-persistent start
Add some SWAP space (if needed)
Create a file and swap on it:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
And check if its OK:
swapon -s
free
Make it persistent on /etc/fstab
:
/swapfile none swap sw 0 0
Install rbenv
and ruby
(as deploy)
export RUBY_VERSION = 2.3.0
git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
exec $SHELL -l
rbenv install $RUBY_VERSION
rbenv global $RUBY_VERSION
gem install bundler
rbenv rehash
wget https://raw.github.com/ryanb/dotfiles/master/gemrc -O .gemrc
echo 'export RAILS_ENV="production"' >> ~/.bash_profile
Edit /etc/mysql/my.cnf and set these lines at the appropriate places:
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
Then relaunch MySQL:
systemctl restart mysql
Create MySQL database for the project
Launch the MySQL CLI client:
mysql -u root -p
Use a generated password and store it somewhere. Then:
mysql> create database `example`;
mysql> create user deploy;
mysql> grant all on `example`.* to 'deploy'@'localhost' identified by 'SuperGeneratedPassword';
mysql> flush privileges;
Setup Capistrano in your project
Gemfile
group :development do
gem 'capistrano'
gem 'capistrano-rails'
gem 'capistrano-rbenv'
gem 'capistrano-bundler'
gem 'capistrano-passenger' , '0.0.2'
end
Capfile.rb
# Load DSL and Setup Up Stages
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/bundler'
require 'capistrano/rbenv'
require 'capistrano/rails/migrations'
require 'capistrano/rails/assets'
require 'capistrano/passenger'
# require 'whenever/capistrano'
# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir . glob ( 'lib/capistrano/tasks/*.rake' ). each { | r | import r }
config/deploy.rb
set :scm , :git
set :format , :pretty
set :log_level , :debug
set :pty , true
set :linked_files , []
set :linked_dirs , %w{log tmp/pids tmp/cache tmp/sockets public/system public/assets private/system}
set :keep_releases , 20
set :rbenv_type , :user
set :rbenv_ruby , File . read ( '.ruby-version' ). strip
config/deploy/production.rb :
server 'example.com' , user: 'deploy' , roles: %w{web app db}
set :application , 'example'
set :repo_url , 'git@github.com:aurels/example.git'
set :deploy_to , '/home/deploy/apps/example'
set :branch , 'master'
set :rails_env , 'production'
set :bundle_without , 'development test cucumber'
config/database.yml
production :
adapter : mysql2
database : example
encoding : utf8
username : deploy
password : SuperGeneratedPassword
pool : 1000
Passenger
Install Phusion Passenger .
Server block
Create a file in /etc/nginx/sites-available/example :
server {
listen 80 ;
server_name example.com ;
add_header Strict-Transport-Security "max-age=63072000 ; preload" ; # HSTS
add_header X-Frame-Options "DENY" ; # Deny iframes
rewrite ^/(.*) https://example.com/ $1 permanent ;
}
server {
listen 443 ;
server_name example.com ;
ssl on ;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem ;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem ;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 ; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on ;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4" ;
ssl_session_cache shared:SSL:10m ;
access_log /var/log/nginx/example.access.log ;
sendfile on ;
root /home/deploy/apps/example/current/public ;
gzip on ;
gzip_disable "msie6" ;
passenger_enabled on ;
passenger_ruby /home/deploy/.rbenv/shims/ruby ;
passenger_app_env production ;
passenger_friendly_error_pages off ;
client_max_body_size 300M ;
}
Enable the server block:
ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/example
Then restart nginx:
systemctl restart nginx
Deploy for the first time
bundle exec cap production deploy
Install Certbot :
https://certbot.eff.org/#debianjessie-nginx
certbot certonly --webroot -w /home/deploy/apps/example/current/public -d example.com
Setup automatic renewal in CRON with crontab -e
:
32 5 * * * /usr/bin/certbot renew --no-self-upgrade --post-hook "service nginx restart"
In /etc/logrotate.d/example :
/home/deploy/apps/example/shared/log/*.log {
daily
missingok
rotate 15
compress
delaycompress
notifempty
copytruncate
}
Setup ElasticSearch (option)
Setup repositories , then:
apt-get update
apt-get upgrade
apt-get update && sudo apt-get install elasticsearch
apt-get install openjdk-7-jre
systemctl start elasticsearch
Setup Monit
In /etc/monit/monitrc :
set daemon 10
set mailserver localhost
set httpd port 2812 and allow monit:APassWordForMonit
check filesystem homefs with path /dev/vda1
check process sshd with pidfile /var/run/sshd.pid
check process nginx with pidfile /var/run/nginx.pid
start program = "/etc/init.d/nginx start"
stop program = "/etc/init.d/nginx stop"
check process mysql with pidfile /var/run/mysqld/mysqld.pid
start program = "/etc/init.d/mysql start"
stop program = "/etc/init.d/mysql stop"
include /etc/monit/conf.d/*.cfg
Reload to apply:
monit reload
Setup backups
As root:
git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
exec $SHELL -l
rbenv install 2.1.0
rbenv rehash
rbenv global 2.1.0
gem install backup
rbenv rehash
backup generate:config
backup generate:model -t example
Replace the contents of /root/Backup/models/example.rb with:
Model . new ( :example , 'Backup of example' ) do
[ 'example' ]. each do | db_name |
database MySQL , db_name do | db |
db . name = db_name
db . username = 'deploy'
db . password = 'SuperGeneratedPassword'
end
end
archive :files do | archive |
archive . add '/home/deploy/apps/example/shared/public/system/'
archive . add '/home/deploy/apps/example/shared/private/system/'
end
compress_with Gzip do | compression |
compression . level = 6
end
store_with Dropbox do | box |
box . api_key = 'xxx'
box . api_secret = 'yyy'
box . path = ''
box . keep = 60
end
notify_by Mail do | mail |
mail . on_success = true
mail . on_failure = true
mail . from = 'backup@example'
mail . to = [ 'me@mail.com' ]
mail . domain = 'example.com'
mail . address = 'smtp.sendgrid.net'
mail . user_name = 'example'
mail . password = 'zzz'
end
end
And add this line to root ’s CRONTAB with:
crontab -e
Add this line:
0 0,2,4,6,8,19,12,14,16,18,20,22,23 * * * /bin/bash -l -c 'backup perform --trigger example'