5

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.

hitchhiker
  • 1,099
  • 5
  • 19
  • 44

1 Answers1

3

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.

braza
  • 4,260
  • 1
  • 26
  • 36
  • indeed this solved the issue! Thank you! but if I still want to have a volume can I "protect" somehow .env from being overwritten? – hitchhiker May 11 '19 at 15:40
  • 1
    You could try creating a volume just to the files you want to overwrite, e.g. ` - "./src:/usr/src/app/src", or there is an answer here if you just want to persist certain files or folders like `node_modules` https://stackoverflow.com/a/38601156/1021714 – braza May 11 '19 at 15:47
  • just to understand: because of the `volumes` in docker-compose, when I'd build the image the contents of `/usr/src/app` inside the container would be overwritten by the contents of host's `/usr/src/app`? – hitchhiker May 11 '19 at 16:08
  • 1
    Yes, but only when you run it, not when you build it. So the files won't exist in the docker image, which is why your `docker run` command worked, but when you run it with your current `docker-compose` config, it will mount the volumes at runtime and overwrite `/usr/src/app` with the contents on the host. – braza May 11 '19 at 16:52