In this example we're going to create a new user inanzzz and allow only inanzzz to deploy our application from deployment server to production server. This user must exist in both servers and own the folders where application will be deployed to.


Initial information


Our application is called football.


Deployment server



Production server



Deployment server


I'm adding vagrant files for you to use if you wish.


Vagrantfile


# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
# Box type
config.vm.box = "ubuntu/trusty64"

# Box settings
config.vm.define :vagrant do |vagrant_config|
# Terminal name
vagrant_config.vm.hostname = "deploy"
# IP to access from host machine
vagrant_config.vm.network "private_network", ip: "192.168.99.30"
# Script to run while setting up the box
vagrant_config.vm.provision :shell, path: "bootstrap.sh"
end

# Oracle VM VirtualBox settings
config.vm.provider :virtualbox do |virtualbox_config|
# Box name
virtualbox_config.name = "Football - deploy"
# Alloved RAM
virtualbox_config.memory = 1024
# Allowed CPU core
virtualbox_config.cpus = 1
end
end

Bootstrap.sh


#!/usr/bin/env bash

# BEGIN ########################################################################
echo -e "-- ------------------ --\n"
echo -e "-- BEGIN BOOTSTRAPING --\n"
echo -e "-- ------------------ --\n"

# BOX ##########################################################################
echo -e "-- SYSTEM\n"
echo -e "-- Updating packages list\n"
apt-get update -y -qq

# RUBY #########################################################################
echo -e "-- RUBY\n"
echo -e "-- Fetching Ruby repository\n"
add-apt-repository -y ppa:brightbox/ruby-ng > /dev/null 2>&1

echo -e "-- Updating packages list\n"
apt-get update -y -qq

echo -e "-- Installing Ruby\n"
apt-get install -y ruby2.2 > /dev/null 2>&1

# CAPISTRANO ###################################################################
echo -e "-- CAPISTRANO\n"
echo -e "-- Installing capistrano\n"
gem install capistrano > /dev/null 2>&1
gem install capistrano-ext > /dev/null 2>&1

echo -e "-- Installing capistrano bundler\n"
gem install bundler > /dev/null 2>&1

# GIT ##########################################################################
echo -e "-- GIT\n"
echo -e "-- Installing Git\n"
apt-get install -y git > /dev/null 2>&1

# END ##########################################################################
echo -e "-- ---------------- --"
echo -e "-- END BOOTSTRAPING --"
echo -e "-- ---------------- --"

Create user


root@deploy:~$ adduser inanzzz

Create project folder with permissions


root@deploy:~$ mkdir /projects
root@deploy:~$ ls -l /
drwxr-xr-x 2 root root 4096 Apr 9 19:57 projects

root@deploy:~$ chown -R inanzzz /projects
root@deploy:~$ ls -l /
drwxr-xr-x 2 inanzzz root 4096 Apr 9 19:57 projects

Switch user


root@deploy:~$ su -l inanzzz

GitHub integration


inanzzz@deploy:~$ ssh-keygen -t rsa -b 4096 -C "inanzzz@domain.com"
inanzzz@deploy:~$ eval "$(ssh-agent -s)"
inanzzz@deploy:~$ ssh-add ~/.ssh/id_rsa
inanzzz@deploy:~$ cat ~/.ssh/id_rsa.pub # Add this key to GitHub repository first
inanzzz@deploy:~$ ssh -T git@github.com

Clone repository


inanzzz@deploy:~$ cd /projects/
inanzzz@deploy:/projects$ git clone git@github.com:Inanzzz/football.git

Production server


I'm adding vagrant files for you to use if you wish.


Vagrantfile


# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
# Box type
config.vm.box = "ubuntu/trusty64"

# Box settings
config.vm.define :vagrant do |vagrant_config|
# Terminal name
vagrant_config.vm.hostname = "prod"
# IP to access from host machine
vagrant_config.vm.network "private_network", ip: "192.168.99.50"
# Script to run while setting up the box
vagrant_config.vm.provision :shell, path: "bootstrap.sh"
end

# Oracle VM VirtualBox settings
config.vm.provider :virtualbox do |virtualbox_config|
# Box name
virtualbox_config.name = "Football - prod"
end
end

Bootstrap.sh


#!/usr/bin/env bash

# BEGIN ########################################################################
echo -e "-- ------------------ --\n"
echo -e "-- BEGIN BOOTSTRAPING --\n"
echo -e "-- ------------------ --\n"

# BOX ##########################################################################
echo -e "-- SYSTEM\n"
echo -e "-- Updating packages list\n"
apt-get update -y -qq

# GIT ##########################################################################
echo -e "-- GIT\n"
echo -e "-- Installing Git\n"
apt-get install -y git > /dev/null 2>&1

# END ##########################################################################
echo -e "-- ---------------- --"
echo -e "-- END BOOTSTRAPING --"
echo -e "-- ---------------- --"

Create user


root@prod:~$ adduser inanzzz

Create deployment folder with permissions


root@prod:~# ls -l /
drwxr-xr-x 3 root root 4096 Apr 14 15:48 srv
drwxrwxrwt 5 root root 4096 Apr 14 15:55 tmp

root@prod:~# mkdir /srv/www
root@prod:~# mkdir /tmp/capistrano

root@prod:~# ls -l /srv/
drwxr-xr-x 2 root root 4096 Apr 14 15:48 www
root@prod:~# ls -l /tmp
drwxr-xr-x 2 root root 4096 Apr 14 15:49 capistrano

root@prod:~# chown -R inanzzz /srv/www
root@prod:~# chown -R inanzzz /tmp/capistrano

root@prod:~# ls -l /srv/
drwxr-xr-x 2 inanzzz root 4096 Apr 14 15:48 www
root@prod:~# ls -l /tmp
drwxr-xr-x 2 inanzzz root 4096 Apr 14 15:49 capistrano

Switch user


root@prod:~$ su -l inanzzz

GitHub integration


inanzzz@prod:~$ ssh-keygen -t rsa -b 4096 -C "inanzzz@domain.com"
inanzzz@prod:~$ eval "$(ssh-agent -s)"
inanzzz@prod:~$ ssh-add ~/.ssh/id_rsa
inanzzz@prod:~$ cat ~/.ssh/id_rsa.pub # Add this key to GitHub repository first
inanzzz@prod:~$ ssh -T git@github.com

Authorising user


If you don't do this on deployment server, everytime you try to deploy your application to production server, you'll be asked to enter user's password.


inanzzz@deploy:~$ cat ~/.ssh/id_rsa.pub | ssh inanzzz@192.168.99.50 'cat >> /home/inanzzz/.ssh/authorized_keys'

Capistrano


You must have these files under your project so I'm just showing their content.


Gemfile


source 'https://rubygems.org'

gem 'capistrano', '~> 3.8'

Capfile


require "capistrano/setup"
require "capistrano/deploy"
require "capistrano/scm/git"

install_plugin Capistrano::SCM::Git

deploy.rb


# Locked capistrano version.
lock "3.8.0"

# The name of the application.
set :application, "football"

# The path on the remote server where the application will be deployed.
set :deploy_to, "/srv/www/#{fetch(:application)}"
set :tmp_dir, "/tmp/capistrano"

# The application repository.
set :repo_url, "git@github.com:Inanzzz/football.git"

# Asks branch to deploy.
ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp

# Output styling.
set :format, :airbrussh

# Amount of releases to keep.
set :keep_releases, 5

production.rb


# The settings for remote server.
server "192.168.99.50", user: "inanzzz", roles: %w{app db web}

.gitattributes


# Prevent from being deployed by capistrano

/config export-ignore
Capfile export-ignore
Gemfile export-ignore
Gemfile.lock export-ignore

Install Bundler packages


inanzzz@deploy:~$ su -l root
root@deploy:~$ cd /projects/football/
root@deploy:/projects/football$ bundle install

Deployment


Deploy


Make sure that you are logged in as inanzzz user.


inanzzz@deploy:/projects/football$ bundle exec cap production deploy

Output


00:00 git:wrapper
01 mkdir -p /tmp/capistrano
✔ 01 inanzzz@192.168.99.50 0.376s
Uploading /tmp/capistrano/git-ssh-football-production-inanzzz.sh 100.0%
02 chmod 700 /tmp/capistrano/git-ssh-football-production-inanzzz.sh
✔ 02 inanzzz@192.168.99.50 0.005s
Please enter branch (develop): master
00:02 git:check
01 git ls-remote git@github.com:Inanzzz/football.git HEAD
01 Warning: Permanently added the RSA host key for IP address '192.30.253.113' to the list of known hosts.
01 Error reading response length from authentication socket.
01 1abcb1bb4e67c06e36ffc65db07ca3fa2489ee03 HEAD
✔ 01 inanzzz@192.168.99.50 1.360s
00:04 deploy:check:directories
01 mkdir -p /srv/www/football/shared /srv/www/football/releases
✔ 01 inanzzz@192.168.99.50 0.005s
00:04 git:clone
01 git clone --mirror git@github.com:Inanzzz/football.git /srv/www/football/repo
01 Cloning into bare repository '/srv/www/football/repo'...
01 Error reading response length from authentication socket.
✔ 01 inanzzz@192.168.99.50 2.939s
00:07 git:update
01 git remote set-url origin git@github.com:Inanzzz/football.git
✔ 01 inanzzz@192.168.99.50 0.006s
02 git remote update --prune
02 Fetching origin
02 Error reading response length from authentication socket.
✔ 02 inanzzz@192.168.99.50 1.515s
00:08 git:create_release
01 mkdir -p /srv/www/football/releases/20170409204127
✔ 01 inanzzz@192.168.99.50 0.006s
02 git archive master | /usr/bin/env tar -x -f - -C /srv/www/football/releases/20170409204127
✔ 02 inanzzz@192.168.99.50 0.009s
00:08 deploy:set_current_revision
01 echo "9tg426851299e4bac6f1b51729b623859c10ef9c" >> REVISION
✔ 01 inanzzz@192.168.99.50 0.007s
00:08 deploy:symlink:release
01 ln -s /srv/www/football/releases/20170409204127 /srv/www/football/releases/current
✔ 01 inanzzz@192.168.99.50 0.005s
02 mv /srv/www/football/releases/current /srv/www/football
✔ 02 inanzzz@192.168.99.50 0.005s
00:08 deploy:log_revision
01 echo "Branch master (at 9tg426851299e4bac6f1b51729b623859c10ef9c) deployed as release 20170409204127 by inanzzz" >> /srv/www/footb…
✔ 01 inanzzz@192.168.99.50 0.005s

Result in Production server


inanzzz@prod:~$ ls -l /
total 8
drwxr-xr-x 4 inanzzz root 4096 Apr 9 20:41 srv
drwxrwxrwt 6 root root 4096 Apr 9 21:43 tmp

inanzzz@prod:~$ ls -l /tmp/
total 3
drwxrwxr-x 2 inanzzz root 4096 Apr 9 21:42 capistrano

inanzzz@prod:~$ ls -l /tmp/capistrano/
total 4
-rwx------ 1 inanzzz inanzzz 93 Apr 9 20:41 git-ssh-football-production-inanzzz.sh

inanzzz@prod:~$ ls -l /srv/
total 8
drwxrwxr-x 3 inanzzz inanzzz 4096 Apr 9 20:41 www

inanzzz@prod:~$ ls -l /srv/www/
total 4
drwxrwxr-x 5 inanzzz inanzzz 4096 Apr 9 20:41 football

inanzzz@prod:~$ ls -l /srv/www/football/
total 16
lrwxrwxrwx 1 inanzzz inanzzz 41 Apr 9 20:41 current -> /srv/www/football/releases/20170409204127
drwxrwxr-x 3 inanzzz inanzzz 4096 Apr 9 20:41 releases
drwxrwxr-x 7 inanzzz inanzzz 4096 Apr 9 20:41 repo
-rw-rw-r-- 1 inanzzz inanzzz 106 Apr 9 20:41 revisions.log
drwxrwxr-x 2 inanzzz inanzzz 4096 Apr 9 20:41 shared