It's the last day of the year again, and it feels like a good tradition to write a nice post on this very particular day.
And since a new year could mean a new life, I wanted to refresh a bit the small infrastructure holding together this blog and all of the web apps I need to deploy.
This website was hosted on a small VPS running on an outdated Ubuntu server, running Passenger (Open source) with it's embedded nginx and configurations.
For security reasons I wanted to reinstall everything from scratch, updating Ubuntu and at the same time migrate to Puma, that enables many features that the open source version of Phusion Passenger doesn't provide.
So, remember to backup your database and important files, hit that reinstall VPS button and follow this guide.
Log in as root, then update your system:
sudo apt update
sudo apt upgrade
Then add a new user that you will use to deploy your apps:
adduser deploy
gpasswd -a deploy sudo
It's then better to disallow root ssh login editing the ssh config file:
sudo nano /etc/ssh/sshd_config
Search for, and edit like so:
# Authentication:
PermitRootLogin no
sudo service ssh restart
Install nginx: on Ubuntu is as simple as:
sudo apt install nginx
Install rvm: go to the secure install page and follow the instruction. In my case, these worked out beautifully:
# Install mpapis public key (might need `gpg2` and or `sudo`)
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
# Download the installer
\curl -O https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer
\curl -O https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer.asc
# Verify the installer signature (might need `gpg2`), and if it validates...
gpg --verify rvm-installer.asc &&
# Run the installer
bash rvm-installer stable
You could install rvm using apt, just remember this will not install it in users folder: this means that in your Capistrano deploy you need to add something like:
set :rvm_type, :system
If you use node and npm, it's better to use a version manager like nvm.
Follow it's instruction for installation, but it's basically a one-liner:
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
Then install your preferred ruby (and bundler) and node binaries.
In my case:
nvm install 8.9.3
rvm install 2.4.2
gem install bundle
Now, we need PostgreSQL:
sudo apt-get install postgresql postgresql-contrib libpq-dev
Then create it's user:
sudo -u postgres createuser -s your_pg_user
sudo -u postgres psql
postgres=# \password your_pg_user
postgres=# \q
Create the required directories for your apps.
On you deploy user home folder, you can:
mkdir apps
mkdir apps/your_rails_app
mkdir apps/your_rails_app/shared
mkdir apps/your_rails_app/shared/config
touch apps/your_rails_app/shared/config/database.yml
And you want to paste in something like this:
production:
database: your_rails_app_production
adapter: postgresql
username: your_pg_user
password: your_pg_user_password
host: localhost
encoding: unicode
port: 5432
pool: 5
This is also the right time to place inside the shared folder every other file you may need to keep between releases.
Now, head into your rails app, and make sure you are using these gems:
gem 'capistrano', '~> 3.10.0'
gem 'capistrano-rails', '~> 1.1.6'
gem 'capistrano3-puma', '~> 3.1.1'
gem 'capistrano-rvm'
gem 'capistrano-nvm'
And set up as you need your deploy:
set :application, 'your_rails_app'
set :repo_url, 'git@github.com:you/your_rails_app.git'
set :deploy_to, '/home/deploy/apps/your_rails_app'
set :rvm_ruby_version, proc { `cat .ruby-version`.chomp }
set :linked_files, fetch(:linked_files, []).push('config/database.yml')
set :linked_dirs, fetch(:linked_dirs, []).push('public/system', 'tmp/pids', 'tmp/sockets', 'tmp/cache', 'log')
set :nvm_type, :user
set :nvm_node, proc { 'v' + `cat .nvmrc`.chomp }
set :nvm_map_bins, %w{node npm}
namespace :deploy do
before :compile_assets, :clear_cache do
on roles(:web), in: :groups, limit: 3, wait: 10 do
within release_path do
execute :npm, "install"
end
end
end
end
A couple of things to note: we are keeping the node and ruby versions from the .nvmrc
and .ruby-version
files respectively: we just need to add the letter 'v' when reading from the file as the node versions folder are prepended by it.
Remember to add 'tmp/pids', 'tmp/sockets', 'log'
as linked dirs, as required by puma (and also other dirs and files you may need).
I also added execution of the npm install
command to install any dependencies there.
We are now getting closer and closer to the actual deploy: the gem capistrano3-puma
provides useful rake tasks to upload many configurations:
bundle exec cap production puma:config
Will upload the puma server configuration on the server, by default in shared/puma.rb
bundle exec cap production puma:nginx_config
Is going to install your site within nginx. This won't probably be ok as is, so be ready to edit it by going into ssh and editing /etc/nginx/sites-enabled/your_rails_app_production
.
Now, since we want to have multiple Rails apps running on the same server, we need a tool called Puma Jungle. These are basically init.d or upstart configuration files that will run Puma as service and handle multiple apps at once.
Once again, the puma capistrano gem will help us and (at least on Ubuntu), can upload the right code with the right search paths with a single command:
bundle exec cap production puma:jungle:setup
We just need to check the puma.conf file on the server, and also add the folder of your puma.rb file like so:
nano /etc/puma.conf
/home/deploy/apps/your_rails_app/current,deploy,/home/deploy/apps/your_rails_app/shared/puma.rb
Now, if everything was done correctly, on your local machine you can just:
bundle exec cap production deploy
And manage your jungle with:
bundle exec cap production puma:jungle:start
bundle exec cap production puma:jungle:stop
bundle exec cap production puma:jungle:restart
bundle exec cap production puma:jungle:status
Hope this was helpful!