Deploy staging and production environments to single server using Capistrano — Rails

Lal Saud
ITNEXT
Published in
5 min readFeb 6, 2020

--

Having multiple environments is one of the most common requirements for me or my clients. Ruby on Rails applications by default come with three environments — development, test and production. Development and test environments are setup in local or developers computer. Production is a server environment where the finalized application code is deployed to the world.

Deploy Rails multiple environments with Capistrano/Passenger/Nginx

However, I have worked in many projects where we needed more than one production like environment. At least, there used to be two environments — staging and production. The team can test features in staging or pre-production environment before they make to production environment and used by real customers.

In the early stages of project development, when resources are not too big, I generally like to use same server machine to configure multiple environments. This way we can same some money as well as keeping the complexity of configuring server to one. Once the application grows big or resource requirements are high enough, then we can always migrate to better and bigger infrastructure.

In this post, I will explain and provide some configuration options to deploy different environments a single server. For me, configuring a server for multiple environments in the beginning was not an easy task. I had to go through several google searches and blog posts, however it is worth taking all that time for me. Here, I have tried to keep all the configuration as simple as possible, so it should work for different project specific settings.

This is not a blog post about how to setup Capistrano or deploy your Ruby on Rails application to production server. You can find several posts and tutorials for deploying your application to your own server.

So, let’s start with assuming that we can already deploy one environment to our server.

Prerequisite

By this step, I am assuming you already have a working Capistrano deploy environment setup for a single environment. However, you don’t have another environment setup to the same server. For eg. — you can execute

cap production deploy

to deploy your production application and it deploys successfully. But

cap staging deploy

doesn’t deploy your application in the same server and you want to configure that.

If you don’t have any of the environments setup or working, I suggest you to stop here and look at this awesome tutorial on gorails.com. I am using gorails tutorial as a base and will be modifying some configurations to setup multiple environments.

Planning

  1. We will create two subdomains — staging.[your-domain].com & production.[your-domain].com, where we will point our applications. Of course, replace [your-domain] with your actual domain.
  2. We will use two different GitHub branches for different environments:
    - master branch for production
    - staging branch for staging
  3. I will be using following tech stack:
    - Digital Ocean ubuntu 18.04 server droplet
    - rbenv with ruby 2.7.0
    - rails 6.0.2 application
    - nginx web server with passenger for rails app
    - PostgreSQL database server
    - Capistrano for deployment

Step 1 — Create subdomains

Go to your DNS zone editor and add two A records that point to same public IP of your server. This is how I did it:

DNS records

Step 2 — Create nginx site configuration files for both apps

Now ssh login to your server and go to nginx sites-enable folder. Then create/update the configuration files for both applications. Here is how I did it:

$> ssh deploy@1.2.3.4
$> cd /etc/nginx/sites-enabled
$> sudo nano production.[your-domain].com
$> sudo nano staging.[your-domain].com

The actual content for both files are as:

staging.your-domain.com
production.your-domain.com

After updating both files, check nginx configuration and restart succeeds with following commands:

$> sudo nginx -t
$> sudo service nginx start

Step 3 — Change deploy scripts

All right, it’s time to make changes to Capistrano deploy script in the rails application. Here is how my config/deploy.rb file looks:

config/deploy.rb

Also I have two environment related files — config/deploy/staging.rb and config/deploy/production.rb. Here are the contents of both files:

config/deploy/staging.rb
config/deploy/production.rb

Step 4 — Database setup

To setup database, we need to have staging and production blocks in the database.yml file. Here is my config/database.yml file looks.

config/database.yml

Note here that I have both staging and production blocks. Also, I am using rails credentials file to save multi environment credentials. You can edit credentials file with your editor and update those environment variables like this:

$> EDITOR="atom --wait" rails credentials:edit

Here is how my credentials file looks, when I open it:

config/credentials.yml.enc file opened in editor

If you are not using credentials file to store your variables, then you have to store staging and production environment variables in server and make sure you have both databases configured correctly.

Step 5 — Other setups

Ok, much of the work is done. Few things remaining, let’s quickly setup those.

i. Define staging environment for Rails— Rails apps by default have 3 environments — development, test and production, defined in config/environments folder. We have one more environment here — staging. So we need to define staging environment config. For this, we can simply production.rb file to staging.rb, like this:

$> cp config/environments/production.rb config/environments/staging.rb

ii. Define staging environment for Webpacker — Rails 6 also requires setting environment for webpacker. You need to add staging section in config/webpacker.yml file. You can simply copy the production block from the same file and edit for staging. Also you need to create config/webpack/staging.js file. You can copy the contents from config/webpack/production.js into staging.js file.

Step 6 — Deploy

That’s all. We are all setup. Let’s deploy both environments with following commands:

$> cap staging deploy
$> cap production deploy

The first time running, you might get an error saying linked files are missing. As we have database.yml and master.key files shared in shared/config folder. Please create or update those files with the content from your application, if you get that error message.

Try again deploying to both environments and everything should work this time.

Now let’s open the browser and try browsing both apps at: http://staging.your-domain.com and http://production.your-domain.com. Both apps should load correctly and you should see different contents as you have pushed in both branches. You can also check using curl in terminal like this:

$> curl -i http://staging.your-domain.com
$> curl -i http://production.your-domain.com

Check logs — If something goes wrong or the websites didn’t load correctly, it’s always a good idea to check logs. You can check for nginx logs or application logs. Nginx logs are generally located at /var/log/nginx, application logs are located at /home/deploy/[environment]/current/log

That’s it. Congratulations! by now you should have a working server with multiple environments. You can follow the same settings to deploy different applications into single server, with slight changes. Please drop comments if you have any feedback.

--

--