Search code examples
node.jsdockerenvironment-variablesnpm-installdotenv

Why dotenv is not loaded in docker but works fine in host machine?


I want to build an Docker image which will run my node.js application. I'm using dotenv npm package to store environment variables. The .env is located in the root of the project. When I run my npm script which starts the application (npm run start_in_docker) from host machine everything works perfectly. However when I run Docker container I get the following error:

internal/modules/cjs/loader.js:584
    throw err;
    ^

Error: Cannot find module 'dotenv'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:582:15)
    at Function.Module._load (internal/modules/cjs/loader.js:508:25)
    at Module.require (internal/modules/cjs/loader.js:637:17)
    at require (internal/modules/cjs/helpers.js:22:18)
    at Object.<anonymous> (/usr/src/app/src/server/main.js:3:1)
    at Module._compile (internal/modules/cjs/loader.js:701:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:712:10)
    at Module.load (internal/modules/cjs/loader.js:600:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:539:12)
    at Function.Module._load (internal/modules/cjs/loader.js:531:3) 

This is my Dockerfile:

FROM node:10-alpine

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN apk add --update python
RUN apk add --update vim
RUN apk add --update sqlite
RUN npm install --only=production

# Bundle app source
COPY . .

EXPOSE 3000

RUN npm install --unsafe-perm=true sqlite3 -g
RUN npm install pm2 -g
# run node-gyp to build sqlite3
RUN cd /usr/src/app/node_modules/sqlite3 && npm run install
WORKDIR /usr/src/app

This is my docker-compose.yml file which runs the Dockerfile above:

version: '3'
services:
  appstore-bl-server:
    container_name: appstore-bl-server-production
    build:
      dockerfile: Dockerfile-prod
      context: .
    volumes:
      - ".:/usr/src/app"
    ports:
      - "3000:3000"
    networks:
      - appstore-net
    command: npm run start_in_docker

networks:
  appstore-net:
    driver: bridge

This is my package.json scripts section:

"scripts": {
    "start_in_docker": "NODE_ENV=development DEBUG=server:*,classes:*,middleware,utils node src/server/main.js",
}

and this is how I require dotenv in my main.js file:

require('dotenv').config();

I tried specifying the absolute path in ('dotenv').config() but it didn't work. What's strange is that if I run the container shell (docker run -it myimage /bin/ash) and then run npm run start_in_docker it will also work. dotenv is in my package.json of course.


Solution

  • I think this is caused by:

    volumes:
      - ".:/usr/src/app"
    

    in your docker-compose file. This links the current dir to the app folder, meaning everything including node_modules is replaced.

    Try removing the volumes from docker-compose and see if it works.

    When you run docker run -it myimage /bin/sh it will not link the volume, meaning it will be using the correct node_modules, which is why it works when using that.