Sir hennihau
Sir hennihau

Reputation: 1774

Node + Docker Compose: Development and production setup

I'm looking for a solution to have both a development and a production environment in my project using docker, docker-compose and nodejs.

How do I approach this?

Basically what I want is a command to start my docker production environment, and a command to start my development environment (which could use nodemon for example).

Here is my Dockerfile

FROM node:13-alpine

RUN mkdir /app

WORKDIR /app

COPY . /app

RUN npm install

RUN npm run build

EXPOSE 1234

CMD ["npm", "run", "prod"] # <--- Have a possibility to run something like "npm run dev" here instead

docker-compose.yml

version: "3"
services:
    findus:
        build: .
        ports:
            - "1234:1234"
        links:
            - mongo
        container_name: myapp
    mongo:
        image: mongo
        restart: always
        ports:
            - "4444:4444"

package.json

// ...
    "scripts": {
        "build": "tsc",
        "dev": "nodemon source/index.ts",
        "prod": "node build/index.js"
    },
// ...

Upvotes: 15

Views: 21764

Answers (3)

Michał Krzywański
Michał Krzywański

Reputation: 16900

You can make use of entrypoint and pass the command to the docker container. Then you can use docker-compose inharitance to launch compose for the environment that you want and append command to the entrypoint.

Dockerfile :

FROM node:13-alpine

RUN mkdir /app

WORKDIR /app

COPY . /app

RUN npm install

RUN npm run build

EXPOSE 1234

ENTRYPOINT ["npm", "run"]

Main docker-compose.yml :

version: "3"
services:
    findus:
        build: .
        ports:
            - "1234:1234"
        links:
            - mongo
        container_name: myapp
    mongo:
        image: mongo
        restart: always
        ports:
            - "4444:4444"

And then have two docker-compose files to append the command passed to the image entry point. For development - docker-compose.dev.yml :

version: "3"
services:
    findus:
        command: dev

and docker-compose.prod.yml :

version: "3"
services:
    findus:
        command: prod

Then to launch dev environment :

docker-compose  -f docker-compose.yml -f docker-compose.dev.yml up    

and for prod environment :

docker-compose  -f docker-compose.yml -f docker-compose.prod.yml up   

So the command will be appended to the ENTRYPOINT instruction.


Also this approach could work with enviroment variables if you wanted to pass the command as environment variable. You can find more information in the docs.

Upvotes: 13

Adi Dembak
Adi Dembak

Reputation: 2536

You can create a structure like this:

docker-compose.yml -->
      docker-compose.dev.yml
      docker-compose.prod.yml

Where the base configuration resides in docker-compose.yml, while environment-specific info such as ports or user credentials would be in docker-compose.dev.yml or docker-compose.prod.yml

And then you can run the dev environment with:

docker-compose \
    -f docker-compose.yml \
    -f docker-compose.dev.yml \
    up -d

Or the prod environment with:

docker-compose \
    -f docker-compose.yml \
    -f docker-compose.prod.yml \
    up -d

Upvotes: 1

Andr&#233; Oliveira
Andr&#233; Oliveira

Reputation: 1123

One way to do it is to create two "targets" on your Dockerfile like this:

Dockerfile:

FROM node:13-alpine As development

RUN mkdir /app

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build

FROM node:13-alpine as production

ARG NODE_ENV=production
ENV NODE_ENV=${NODE_ENV}

WORKDIR /app

COPY package*.json ./

RUN npm install --only=production

COPY . .

COPY --from=development /app/dist ./dist

CMD ["node", "dist/index.js"]

And them on your docker-compose run only the Development version:

version: '3.7'

services:
  main:
    container_name: main
    build:
      context: .
      target: development
    command: npm run dev
...

So on your dev environment, you can run:

docker-compose up

and them in prod you can run docker directly with

docker run --target production

I'm assuming that when you run "npm run build" you are generating a dist/production folder so it's better to run node there instead of running straight at index.js on your root folder.

Upvotes: 2

Related Questions