m-ketan
m-ketan

Reputation: 1308

node dotenv won't work with pm2

I have an application where locally (without pm2) all the environment variables in the .env file work just fine using dotenv.

But on the server where I'm using pm2 to run the app, the environment variables remain undefined.

The pm2 commands I'm using to run the app on server are:

pm2 start myapp/app.js
pm2 startup
pm2 save

Upvotes: 45

Views: 58196

Answers (11)

Wiatagan Paz
Wiatagan Paz

Reputation: 91

Using ecosystem.config.js, change the interpreter, use package.json scripts.

module.exports = {
  apps: [
    {
      name: 'myapp',
      watch: ['myapp/app.js'],
      node_args: '--cwd myapp start',
      interpreter: 'yarn',
    },
  ],
}

Upvotes: 0

Zsolt
Zsolt

Reputation: 71

2024 Update

With Node.js >= 20.6.0 there is no need for the dotenv package and you can directly load an env file:

node --env-file .env

Using ecosystem.config.js, this requires passing the extra node_args to node:

module.exports = {
  apps: [
    {
      name: "my app",
      script: "./index.js",
      cwd: "/path/to/app",
      node_args: "--env-file <path-relative-to-cwd>/.env",
      ...
    },
  ],
}

Upvotes: 5

Mel Habip
Mel Habip

Reputation: 134

I came here with the same issue as well. My app would run locally but fail when deploying with PM2.

Reading the above, I came up with a super simple fix:

BEFORE:

import dotenv from 'dotenv';
dotenv.config();

AFTER

import dotenv from 'dotenv';
dotenv.config({
  path: '../.env'
});

note: ensure that this is the very first piece of code in your app.

Upvotes: 1

ZiiMakc
ZiiMakc

Reputation: 36986

You can parse .env using dotenv lib end set them manually in ecosystem.config.js

ecosystem.config.js:

const { calcPath, getEnvVariables } = require('./helpers');

module.exports = {
  apps: [
    {
      script: calcPath('../dist/app.js'),
      name: 'dev',
      env: getEnvVariables(),
    },
  ],
};

helpers.js:

const path = require('path');
const dotenv = require('dotenv');
const fs = require('fs');

function calcPath(relativePath) {
  return path.join(__dirname, relativePath);
}

// this function will parce `.env` file but not set them to `process.env`
const getEnvVariables = () => {
  const envConfig = dotenv.parse(fs.readFileSync(calcPath('.env')));

  const requiredEnvVariables = ['MODE'];

  for (envVariable of requiredEnvVariables) {
    if (!envConfig[envVariable]) {
      throw new Error(`Environment variable "${envVariable}" is not set`);
    }
  }

  return envConfig;
};

Upvotes: 4

mattdlockyer
mattdlockyer

Reputation: 7314

None of this worked for me because I was using cluster mode.

I installed dotenv as dev dependency at the root (I was using yarn workspaces too).

Then I did this:

require('dotenv').config({ path: 'path/to/your/.env' })

module.exports = {
    apps: [
        {
            name: 'app',
            script: 'server/dist/index.js',
            instances: 2,
            exec_mode: 'cluster',
            instance_var: 'APP_INSTANCE_SEQ',
            // listen_timeout: 10000,
            // restart_delay: 10000,
        }
    ]
}

Upvotes: 2

Andy Lorenz
Andy Lorenz

Reputation: 3084

A good pattern here is to remove dotenv from your code and "require" it on the command line. This makes your code nicely transportable between any environment (including cloud-based) - which is one of the main features of environment variables.

Note: you will still need to install dotenv in your project via npm when running it on a server.

a) code up your .env file alongside your script (e.g. app.js)

b) to run your script without pm2:

node -r dotenv/config app.js

c) in pm2.config.js:

module.exports = {
  apps : [{
    name      : 'My Application',
    script    : 'app.js',
    node_args : '-r dotenv/config',
    ...
  }],
}

and then pm2 start pm2.config.js

note: the use of dotenv/config on the command line is one of the best practices recommended by dotenv themselves

edit 2021: for completeness - as my answer has got some ticks, I wanted to add a 4th option to the list:

d) combined pm2/env config

module.exports = { apps : [{
  name      : 'My Application',
  script    : 'app.js',
  env       : {
    PORT: 5010,
    DB_STRING: 'mongodb://localhost:27017',
    ...
  },
}]};

This will be useful if you are treating your pm2.config as environmental configuration and outside of git etc. It just negates the need for a separate .env, which may suit you. It negates the need for dotenv completely as pm2 injects the env variables into your script's process

Upvotes: 40

Subham kuswa
Subham kuswa

Reputation: 456

This was my project setup..

/src/app.ts

which than compiled into dist folder.

/dist/app.js

my .env file was outside dist folder so it wasn't accessible.

this is the command i tried. pm2 start app.js --env=.env

Upvotes: 0

Ahmed
Ahmed

Reputation: 1359

you have kill you pm2 process first

try

pm2 kill

then restart pm2 using

pm2 start app.js

Upvotes: 19

wzy
wzy

Reputation: 81

I had the same problem but it wasnt explained clearly so here is the solution based on github user vmarchaud comment. This also fixes the issue people had with @Andy Lorenz solution.

In my case i wanted to create an ecosystem file for multiple apps but i was keep getting

Error: Cannot find module 'dotenv/config'

The solution was easy. You have to declar cwd, aka the project folder where the dotenv/config will be read from.

module.exports = {
  apps: [{
    name: 'app1 name',
    script: 'app1.js',
    cwd: '/path/to/folder/',
    exec_mode: 'fork_mode',
    node_args: '-r dotenv/config',
  }, {
    name: 'app2 name',
    script: 'app2.js',
    cwd: '/path/to/folder/',
    instances: 'max',
    exec_mode: 'cluster',
    node_args: '-r dotenv/config',
  }],
};

Upvotes: 8

AlexZeDim
AlexZeDim

Reputation: 4352

I use a much simpler version of @Marcos answer:

.env
app.js

for example we need to store token in .env file and pass it right to app.js: inside .env

token=value

inside app.js:

require('dotenv').config();
console.log(process.env.token)

Also, don't forget. If you add .env file to .gitignore and then git pull you repo on VPS or smth, you need to copy .env file manually, otherwise your app won't work.

And in some cases it's important in what area you are using your config, so make sure that NODE_ENV=production string is added to your .env file.

After all you could use pm2 start app.js right from your app's folder.

Upvotes: 0

Marcos Casagrande
Marcos Casagrande

Reputation: 40444

dotenv will read .env file located in the current directory.

When you call pm2 start myapp/app.js it won't search for myapp/.env.

.env // It will try to load this, which doesn't exist
myapp/
   app.js

So you have two solutions

use path option:

const path = require('path'); 
require('dotenv').config({ path: path.join(__dirname, '.env') });

Or call your script from inside myapp/

pm2 start app.js

Upvotes: 98

Related Questions