Multi Environment Deployment with CI/CD
Automating your app deployment helps you to focus and eliminate the redundant steps. Sometimes your app has to have multiple environments for various reasons. Let us consider three environments in our case: development, staging, and production with environment-specific sensitive data.
To demonstrate how to setup multi-env deployment I would consider the following things:
- ExpressJS project or anything of your choice.
- Gitlab
Your Project
In your project, there must be a place to save your environment-specific files. In NodeJS I’m going to use dotenv
NPM package for it. Create three files .env.development
, .env.staging
, .env.production
which will have environment-specific values.
In your app.js file or project startup file you must be load the env file (if required).
// existing code
const dotenv = require('dotenv');
dotenv.config({ path: `.env.${process.env.NODE_ENV}` });
...
In your local system you must specify the environment name when starting the project, something like this:
NODE_ENV=development nodemon ./bin/www
Gitlab
Create a repo and push your code to Gitlab and then we are ready to work on the deployment process. You create a separate branch for every environment.
Tip: Never commit your environment files as they have sensitive data
Under Project’s Settings > CI/CD > Variables. Upload the sensitive data by encoding it and saving the private key too in the variable. Make sure you’ve given a unique name to the key here so that we can differentiate between the environments.
Now let’s create the deployment script. Create .gitlab-ci.yml
file in the root directory of your project and paste the following code. I’ve selected ubuntu:latest
as my docker image but you may choose anything according to your preference.
image: ubuntu:latestvariables: WORK_DIR: ${CI_PROJECT_NAME} BRANCH: ${CI_COMMIT_REF_NAME}
Now let’s add script for staging environment.
staging: stage: staging environment: name: 'staging' script: - echo "$STAGING_ENV" > .env.staging.tmp - # decode and rename the file to .env.staging - # deploy to server - # restart server only: - staging
We are creating a basic structure which is required for gitlab’s runner to understand our deployment procedure. The core logic is under script
, where STAGING_ENV
is the environment variable declared in gitlab ci/cd variables earlier. It can be accessed like any other variable in bash.
The sensitive data is stored in .env.staging.tmp
and then we decipher the contents to obtain the actual keys and rename the file to .env.staging
. Deploy the code to your remote server via SSH, SCP, RSync, etc., and restart the server to get the latest changes.
Note: We only run the stage staging
where there is a change in that branch.
Finally paste the below code which enables the stage which we have just created
stages: - staging
Your .gitlab-ci.yml
should look something like below
image: ubuntu:latestvariables: WORK_DIR: ${CI_PROJECT_NAME} BRANCH: ${CI_COMMIT_REF_NAME}stages: - staging
staging: stage: staging environment: name: 'staging' script: - echo "$STAGING_ENV" > .env.staging.tmp - # decode and rename the file to .env.staging - # deploy to server - # restart server only: - staging
Voila, you can repeat this process for production or test environment.
Checkout my repo to build & deploy your angular application from gitlab here.
You can use similar logic on Bitbucket Pipelines or any CI/CD for that instance to setup a multi-env deployment.
Do share your feedback by commenting below :)