PM2
reading time 5 mins
This guide will show you to use Doppler to supply app config and secrets to your PM2 managed Node.js applications.
You can also check out a complete working example at https://github.com/DopplerUniversity/pm2
Prerequisites
- You're familiar with PM2
- The Doppler CLI is installed
- You're familiar with provising machines for deployment and CI/CD systems
Service Token
Accessing your secrets in production or CI/CD environment requires a Service Token to provide read-only access to a specific config and we'll cover how to provide this in the Deployment section.
Scripts
The easiest way to use Doppler with PM2 is by creating either a bash (recommended) or Node.js script that uses the Doppler CLI to inject secrets as environment variables into your process.
Using bash is recommended unless you're in an environment where bash doesn't exist (e.g. Windows without WSL).
Create a script that will call doppler run
using the following naming conventions (PM2 only recognizes .sh
, .js
, and .py
files):
- bash:
bin/doppler-run.sh
- node:
bin/doppler-run.js
doppler run -- npm start
import { spawn } from 'child_process'
const doppler = spawn('doppler', ['run', '--', 'npm', 'start'])
doppler.stdout.on('data', (data) => console.log(`${data}`))
doppler.stderr.on('data', (data) => console.error(`${data}`))
Now start your application using PM2:
pm2 start bin/doppler-run.sh
pm2 start bin/doppler-run.js
To stop or delete your application, use the name of the script without the file extension:
pm2 stop doppler-run
pm2 delete doppler-run
Cluster Mode
To run your PM2 application in cluster mode, the previously shown approach using scripts won't work as required because PM2 expects the supplied script to be a Node.js application.
Working around this is easy! You'll just need to add a small amount of new code to fetch secrets using the Doppler CLI from within your application.
Your ecosystem.config.js
file will likely look similar to the below.
module.exports = {
apps : [{
name : "app",
script : "./src/app.js", // Must be Node.js application code
instances : 2,
exec_mode : "cluster"
}]
}
Add a file named doppler.js
to your application that will be responsible for fetching secrets in JSON format from the CLI:
const execSync = require('child_process').execSync
function fetchSecrets() {
try {
return JSON.parse(
execSync('doppler secrets download --no-file --format json')
)
} catch (error) {
process.exit(1)
}
}
module.exports = { fetchSecrets }
Then use this code in your application entry point file to fetch secrets prior to configuring and bootstrapping your application:
const doppler = require('./doppler')
const secrets = doppler.fetchSecrets()
// Configure and initialize your application
With all the pieces in place, you can now start your application in cluster mode by running:
pm2 start ecosystem.config.js
Docker
Using Doppler with PM2 in a Docker container simply requires the installation of bash and the Doppler CLI (which is likely already installed if using a non-Alpine-based Node image).
Below is a complete and production-ready example Dockerfile
:
FROM node:lts-alpine
# NOTE: PM2 requires bash to be installed
RUN apk add bash
# Install Doppler CLI
RUN (curl -Ls --tlsv1.2 --proto "=https" --retry 3 https://cli.doppler.com/install.sh || wget -t 3 -qO- https://cli.doppler.com/install.sh) | sh -s -- --verify-signature
WORKDIR /usr/src/app
ENV PATH=$PATH:/usr/src/app/node_modules/.bin
COPY package.json package-lock.json ./
RUN npm clean-install --only=production --silent --no-audit && mv node_modules ../
COPY . .
# The `pm2-runtime` script is required when using Docker
CMD ["pm2-runtime", "bin/doppler-run.sh"]
Then to start the container, pass in a Doppler Service token as the DOPPLER_TOKEN
environment variable:
# Presumes the DOPPLER_TOKEN environment variable is set in the hosting environment
docker run -e DOPPLER_TOKEN="$DOPPLER_TOKEN" your/docker-image
Deployment
Configuring the Doppler CLI in production requires a Doppler Service Token and there are two ways to supply this to your PM2 application.
Option 1: Preconfiguring the Doppler CLI (preferred)
Pre-configuring the Doppler CLI when provisioning the production machine is the most secure option and works by scoping the Doppler Service Token to the location of the application directory. This only needs to be performed once.
For example, if your app is checked out to /home/ubuntu/your-app
then the command would be the following (replacing dp.st.prd.xxxx
with your Service Token):
doppler configure set token dp.st.prd.xxxx --scope /home/ubuntu/your-app
# Security measure by removing the Service Token value from bash history
history -c
Option 2: ecosystem.config File
You can use an ecosystem.config
file to store the service token but do not commit this to your repository.
Instead, this value should be dynamically inserted by a CI/CD deployment job. For example, this could be the run
step in a GitHub Action:
# Uses bash heredoc syntax - https://linuxize.com/post/bash-heredoc/
cat << EOF > ecosystem.config.js
module.exports = {
apps: [{
script: 'bin/doppler-run.sh',
env: {
DOPPLER_TOKEN: '${{ secrets.DOPPLER_TOKEN }}'
}
}]
};
EOF
Awesome Work!
Now you know how to use Doppler to supply secrets to your PM2 managed Node.js applications.
Updated 2 months ago