Link Search Menu Expand Document

Advanced deploy with Capistrano

In this guide, we’ve explained how to install Decidim directly in the server. That’s fine to start, but in terms of maintainability it is not very scalable.

In here we will use GIT and Capistrano have a nice record of all our changes in our app. This will allow to make changes and safely revert them in case of need.

This means, that we won’t be working on the remote server directly any more, the workflow now will be to do all the changes locally and then execute capistrano to let them do all the work for you:

In Your computer:
   change files 👉 commit in GIT 👉 Deploy in Capistrano
In the server:
                                             👇
                                      Upload changes to server
                                      Run migrations
                                             👇
                                      Reload the server to point
                                      the new installation

Preparation

We will assume that the current installation is being done either following the Ubuntu guide or using the automated script.

If your case is a little different, you’ll need to adapt some of the steps. Some of the steps will be performed in your computer and others in the server (in order to bring the files to you).

Server actions

The first we need to do, is to use some version control software to allow to track any change in our installation and move it between places. We’ll use GIT and Github as a remote repository.

First, log in the server, ensure that you have git installed:

ssh decidim@my-decidim.org
sudo apt install git

Now, go to your installation and make sure that your .gitignore file includes the file config/application.yml which contains sensitive data and we won’t to track in GIT.

Check with:

grep "application.yml" ~/decidim-app/.gitignore

Execute this if not found:

echo "/config/application.yml" >> ~/decidim-app/.gitignore

The next action is to prepare the directory structure for the deployment with Capistrano. Until now, we’ve just used a single folder with the application. Any changes are made there and if something goes wrong… , well it can turn quickly into a mess.

The way Capistrano deals with deployments (and many other tools) is to separate application releases in different folders with some shared content between them (ie: configuration files or user uploads). Then creates a symbolic link to the last release and restarts the server. This way, if something goes wrong is very easy to simply point the server back to a previous release with minimal downtime.

Let’s get to it then, we will create a new deployment folder, for instance app-deploy and, in it, a config folder in which we will copy our configuration files:

mkdir -p ~/app-deploy/shared/config
mkdir -p ~/app-deploy/shared/public
mkdir -p ~/app-deploy/shared/log
cp ~/decidim-app/config/application.yml ~/app-deploy/shared/config/
cp ~/decidim-app/config/database.yml ~/app-deploy/shared/config/

Now, we will create the uploads and logs folder and move our stuff there:

mv ~/decidim-app/public/uploads ~/app-deploy/shared/public/
mv ~/decidim-app/log ~/app-deploy/shared/

In case we don’t want to break our current server just yet, we will add some temporary symlinks to the moved directories in the old system:

ln -s ~/app-deploy/shared/public/uploads ~/decidim-app/public/uploads
ln -s ~/app-deploy/shared/log ~/decidim-app/log

Finally, we will prepare the server to look a the new directory, to start we will point the current release to our old decidim-app directory. Then, after the first Capistrano deploy, this will change automatically.

First, create a symlink to our current installation:

ln -s ~/decidim-app ~/app-deploy/current

We need to reconfigure where nignx will look for files, assuming we’ve followed the “Ubuntu 18.04” guide, edit decidim.conf Nginx file:

sudo nano /etc/nginx/nginx/sites-enabled/decidim.conf

Change the line with

root         /home/decidim/decidim-app/public;

with:

root         /home/decidim/app-deploy/current/public;

Let’s check the file is correct with the command:

sudo nginx -t

It should respond something like this:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Then, restart Nginx:

sudo service nginx restart

Here, if you have any other scripts that deal with the former route (~/decidim-app), you should edit them to point to the new deployment path (~/app-deploy/current/).

If you have followe the “Ubuntu 18.04” guide or used the Automated script, you should look and edit the crontab:

crontab -e

Change any reference to the path ~/decidim-app for /app-deploy/current and ensure to kill the old instance of ActiveJob with this command:

kill -9 $(cat ~/decidim-test/tmp/pids/delayed_job.pid)

At this point, we are ready to move the rest of the action to our computer. From now on, we won’t make changes directly in the server anymore.

Local actions

Now is the time to install Ruby and GIT in your computer as any further operation will be done there only. As this depends heavily on your operative system, you should look up a little what’s the best way in your particular case.

If you are using Ubuntu for instance, you can follow the firsts steps of the installation guide to install the rbenv ruby manager. Refer to their repository for information:

https://github.com/rbenv/rbenv

You also need to install GIT, again, the method vary but it should be a quite straightforward process. Check the official page for information:

https://git-scm.com/book/en/v2/Getting-Started-Installing-Git

So, from now on we’ll assume you have both ruby (with gems) and git configured in your local computer.

Now, we will move the content from the server to our computer with this command:

mkdir decidim-my-app 
cd decidim-my-app
sudo scp -r decidim@myserver.org:decidim-app .

Now that we have the code on our computer, we need to start tracking it by making “commit” in the GIT history. We’ll start with our first commit:

git add .
git commit -m "Initial Decidim installation"

At this, you have the code in your own computer, in the next steps we will add Capistrano to our project and then we will use it to mange the server updates for us.

However, we still need to have a remote repository for GIT, this is a way to centralize our code in an external server, capistrano will use it to download the changes and deploy the application.

You can use Github, Gitlab, Bitbucket, or any other similar provider (you can even use your own servers if you want).

So, let’s say you are using Github, just create an account there and initialize a new repository called, for instance, decidim-my-app (call it something meaningful to you).

After creating your repository, Github will present you with a bunch of instructions, we are interested in the ones with this type of content:

git remote add origin git@github.com:YourUser/decidim-my-app.git
git push -u origin master

Which is exactly what you need to execute after the previous initial commit. This will upload all your file to Github and you are ready to move you continue with Capistrano.

First, install Capistrano globally by executing:

gem install capistrano

If you have succeeded, you should be able to run the next command and get the version of Capistrano installed:

cap --version
Capistrano Version: 3.14.0 (Rake Version: 13.0.1)

Then, modify the Gemfile with and editor of your choice (try VSCode if unsure). And add the “capistrano” lines in the :development section:

...
group :development do
  gem "letter_opener_web", "~> 1.3"
  gem "listen", "~> 3.1"
  gem "spring", "~> 2.0"
  gem "spring-watcher-listen", "~> 2.0"
  gem "web-console", "~> 3.5"

  gem 'capistrano'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano-passenger', '>= 0.1.1'
  gem 'capistrano-rails'
end
...

Then bundle locally:

bundle update

Now, as matter of security, add these lines into the file .gitignore:

# Ignore deploy secrets
/config/deploy.rb
/config/deploy/*
/Capfile

You can now create a new commit with git with the changes:

git add .
git commit -m "Add capistrano gems"
git push

Nice and tidy!, Now is the time to configure Capistrano to let it now about our server. We will start by generating the first automated configuration with the command:

cap install

This will generate some files, the most important are:

Capfile
config/deploy.rb
config/production.rb

We need to modify these files, let’s start with Capfile, We should uncomment or add the next lines:

require "capistrano/rbenv"
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"
require "capistrano/passenger"
require "whenever/capistrano"

Note: The rbenv depends on how you’ve installed ruby in your system. Check that in the official documentation if you are not using rbenv.

Next is to edit the config/production.rb file, we will indicate the servers we are using there (in our case is only one). Let’s add these lines in it at the end of the file:

role :app, %w{decidim@myserver.org}
role :web, %w{decidim@myserver.org}
role :db,  %w{decidim@myserver.org}

Last, is to configure the file config/deploy.rb. In this file we need to add (or in some cases uncomment) several lines, be careful, some might change according to your system:

set :rbenv_type, :user # or :system, depends on your rbenv setup
set :rbenv_ruby, '2.6.6'

set :application, "decidim-my-app"
set :repo_url, "https://github.com/YourUser/decidim-my-app.git"

set :linked_files, fetch(:linked_files, []).push('config/application.yml')
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')

set :passenger_restart_command, '/usr/bin/passenger-config restart-app'

# This will use system configure bundle path, 
# it is safe to remove if you want your gems 
# in the folder ~/app-deploy/shared/bundle
set :bundle_path, nil
set :bundle_without, nil
set :bundle_flags, nil

# Default branch is :master
# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp

# Default deploy_to directory is /var/www/my_app_name
set :deploy_to, "/home/decidim/app-deploy"

We’ve done, if everything is correct we should be able to make our first deploy with Capistrano with the command:

cap production deploy

The output will look like (it will take a while):

00:00 git:wrapper
      01 mkdir -p /tmp
    ✔ 01 decidim@myserver.org 0.137s
      Uploading /tmp/git-ssh-decidim-my-app-decidim.sh 100.0%
      02 chmod 700 /tmp/git-ssh-decidim-my-app-decidim.sh
    ✔ 02 decidim@myserver.org 0.153s
00:00 git:check
      01 git ls-remote https://github.com/YourUser/decidim-my-app.git HEAD
      01 1305546c96c556b6eaacdf9c2a6c952d4e64e092	HEAD
    ✔ 01 decidim@myserver.org 0.677s
00:01 deploy:check:directories
...
...
...
INFO [b0236541] Finished in 0.130 seconds with exit status 0 (successful).

Conclusion

Using an automated tool to deploy your applications is a step forward in terms of maintainability and security. Of course, this is the very basic start point. From this you can use this to split tasks between servers (for instance database in one server and code in another).

Make sure that you read the Capistrano guide to know how to deal with rollbacks for instance:

https://capistranorb.com/documentation/getting-started/rollbacks/