Based on this tutorial but simplified and inlined. Particularly removed any Rails and 3rd party services part, assumed you just need deployment to any Ubuntu machine.
- A functional Rails app
- Hosted Git repository (Github, Bitbucket, Gitlab)
- Cloud hosting account (Digital Ocean, Vultr, Linode, Lightsail)
- Local SSH account
- Setup Ubuntu system
- Install database
- Create deploy user
- Prepare ruby environment
- Setup mina deployment in Rails
- Run app server
- Configure nginx webserver
- BONUS: Configure SSL with certbot
- Set the timezone:
dpkg-reconfigure tzdata
- Update all packages and reboot:
apt-get update && apt-get upgrade && apt-get autoremove && reboot
- Disable SSH password authentication:
nano /etc/ssh/sshd_config && service ssh reload
Change these:
...
PasswordAuthentication no
...
UsePAM no
...
- Check the open ports (should be only SSH):
netstat --listening --tcp
- Enable the Ubuntu firewall so that unconfigured services will not be exposed:
ufw allow 22 && ufw logging off && ufw enable && ufw status
The firewall rules are automatically saved and restored on reboot.
- Install PostgreSQL:
apt-get install postgresql postgresql-contrib libpq-dev
- Edit the configuration and remove the two lines starting with “host ...” that make PostgreSQL listen on a localhost port - a local socket connection is sufficient for Rails:
nano /etc/postgresql/10/main/pg_hba.conf && service postgresql restart
- Create a user and a database for the application:
sudo -u postgres createuser rails-demo
sudo -u postgres createdb rails-demo --owner=rails-demo
(Ignore the “could not change directory to "/root": Permission denied” warnings)
- Create a user for the app:
APP_NAME=rails-demo
adduser $APP_NAME --disabled-password
- Copy your SSH public key to the user home so you can log-in as the app user, for example:
mkdir /home/$APP_NAME/.ssh
cp ~/.ssh/authorized_keys /home/$APP_NAME/.ssh/
chown $APP_NAME.$APP_NAME /home/$APP_NAME/.ssh -R
chmod go-rwx /home/$APP_NAME/.ssh -R
- Log-out and log-in as the app user:
ssh rails-demo@serverip
- Generate a SSH key pair without password as deployment key:
ssh-keygen && cat ~/.ssh/id_rsa.pub
- Add the deployment key to repository.
- Login back as root user in your Ubuntu machine, Install git, nodejs, rng-tools and ruby build dependencies. Install yarn pakage manager for assets compilation:
curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo add-apt-repository ppa:chris-lea/redis-server
sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev dirmngr gnupg apt-transport-https ca-certificates redis-server redis-tools nodejs yarn
apt update && apt install yarn
- Logout, and login as deploy user to install rbenv
cd
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec $SHELL
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
exec $SHELL
-
Log-out and log-in to enable rbenv.
-
Install Ruby and the bundler gem:
rbenv install 2.5.1
rbenv global 2.5.1
ruby -v
gem update --system
gem install bundler
- On your local machine, add Mina, and Mina Puma to your Gemfile:
gem 'mina', require: false
gem 'mina-puma', require: false
- Add a
config/deploy.rb
configuration file to the Rails project:
require 'mina/rails'
require 'mina/git'
require 'mina/rbenv'
require 'mina/puma'
set :application_name, 'appname'
set :domain, '000.000.000.000'
set :user, fetch(:application_name)
set :deploy_to, "/home/#{fetch(:user)}/app"
set :repository, '[email protected]:user/appname.git'
set :branch, 'master'
set :shared_dirs, fetch(:shared_dirs, []).push('log', 'storage', 'tmp/pids', 'tmp/sockets')
set :shared_files, fetch(:shared_files, []).push('config/database.yml', 'config/puma.rb', 'config/master.key')
task :remote_environment do
invoke :'rbenv:load'
end
task :setup do
in_path(fetch(:shared_path)) do
command %[mkdir -p config]
command %[touch "#{fetch(:shared_path)}/config/database.yml"]
command %[touch "#{fetch(:shared_path)}/config/puma.rb"]
command %[chmod -R o-rwx config]
end
end
desc "Deploys the current version to the server."
task :deploy do
invoke :'git:ensure_pushed'
deploy do
invoke :'git:clone'
invoke :'deploy:link_shared_paths'
invoke :'bundle:install'
invoke :'rails:db_migrate'
invoke :'rails:assets_precompile'
invoke :'deploy:cleanup'
on :launch do
# invoke :'puma:restart'
end
end
end
- Let mina create the app folder structure on the server:
mina setup
- On the server, if needed, edit the created configuration files:
nano app/shared/config/database.yml
nano app/shared/config/puma.rb
Sample of database.yml
content:
production:
database: rails-demo
adapter: postgresql
pool: 5
timeout: 5000
Sample of puma.rb
content:
environment "production"
bind "unix:/home/rails-demo/app/shared/tmp/sockets/puma.sock"
pidfile "/home/rails-demo/app/shared/tmp/pids/puma.pid"
state_path "/home/rails-demo/app/shared/tmp/sockets/puma.state"
directory "/home/rails-demo/app/current"
workers 2
threads 1,4
daemonize true
stdout_redirect "/home/rails-demo/app/shared/log/puma.stdout.log", "/home/rails-demo/app/shared/log/puma.stderr.log"
activate_control_app 'unix:/home/rails-demo/app/shared/tmp/sockets/pumactl.sock'
prune_bundler
- To deploy the app to the server, run locally:
mina deploy
Run from your local machine (not server):
$ mina puma:start
$ mina puma:status
$ mina puma:restart
$ mina puma:stop
The puma server is not made to serve HTTP requests directly, so let’s put a nginx web server in front of it:
- Login to server using root access to install nginx:
apt-get install nginx
- Disable the default page:
rm /etc/nginx/sites-enabled/default
- Create a nginx site for the application:
nano /etc/nginx/sites-available/rails-demo
Example configuration:
upstream rails-demo {
server unix:/home/rails-demo/app/shared/tmp/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
server_name example.com;
root /home/rails-demo/app/current/public;
location ~ ^/(assets)/ {
gzip_static on; # to serve pre-gzipped version
expires max;
add_header Cache-Control public;
}
location / {
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://rails-demo;
}
}
server {
listen 80;
server_name www.example.com;
return 301 http://example.com$request_uri;
}
- Enable the site configuration:
ln -s /etc/nginx/sites-available/rails-demo /etc/nginx/sites-enabled/rails-demo
- Reload nginx if the nginx configuration is ok:
nginx -t && service nginx reload
- Enable port 80 in the firewall:
ufw allow 80
- Check if the application responds as expected, check the log files otherwise:
tail /var/log/nginx/error.log /home/rails-demo/app/shared/log/*
- Edit
config/deploy.rb
in the Rails application and enable the service restart command:
on :launch do
invoke :'puma:restart'
end
- Make a visible change in the app, commit the change and re-deploy:
mina deploy
Coming soon