Search code examples
node.jsdockerdocker-composedockerfiledocker-multi-stage-build

Trying to separate dev stage image from prod stage build using mult-stage building


Well, there's this Node.js app that I built using Typescript + ExpressJS, and now I'm trying to setup docker to have both a working development container and a production one.

I'd like to have only one Dockerfile in order to use the multi-stage building process.

My project consists of the root folder, inside of it there are all these dockerfile and compose files, and also my package.json, src/ folder, yarn.lock and etc.

For now I have this:

Dockerfile

FROM node:14.18.1-alpine3.14 as base
ENV APP=/app
WORKDIR $APP
COPY . $APP/
RUN yarn install --frozen-lockfile &&\
    rm -rf "$(yarn cache dir)"
RUN yarn build

FROM base as prod
ENV APP=/home/node/app
WORKDIR $APP
RUN chown node:node $APP
USER node
COPY --chown=node:node --from=base ./app/dist/ $APP/dist/
COPY --chown=node:node package.json yarn.lock $APP/
RUN NODE_ENV=production yarn install --frozen-lockfile &&\
    rm -rf "$(yarn cache dir)"

If I build the base stage only, this is the image I get: dev image

355.65 MB of image size. Now if I check how big the node_modules inside the image is, this is: dev node modules That's ok for a dev node_modules. Checking the layers with dive, I get these: dive dev You can see that 237 MB of those 355 is the node_modules folder.

Now, the production part. If I build to the prod target of the dockerfile, that's my image size: prod image That's even bigger than the dev one. Checking the node_modules size inside of that image: prod node Wen from 237 to only 36 MB, really good But now if I check the layers from that image, that's the result: prod layers The 237 MB node_modules is still there, that's almost the entire image size. How can I avoid having that thresh in my production image? Without that, the result image should be something like 80 MB only, really better than 391.

Please, help me with that!!!


Solution

  • just fixed the problem by replacing FROM base as prod by FROM node:14.18.1-alpine3.14 as prod. The first method was caching all the base layers in the final build.