Maciej Cieślar
Jan 22, 2020 ⋅ 10 min read

Containerized development with NestJS and Docker

Maciej Cieślar A JavaScript developer and a blogger at mcieslar.com.

Recent posts:

Exploring Nushell, A Rust Powered, Cross Platform Shell

Exploring Nushell, a Rust-powered, cross-platform shell

Nushell is a modern, performant, extensible shell built with Rust. Explore its pros, cons, and how to install and get started with it.

Oduah Chigozie
Apr 23, 2024 ⋅ 6 min read
Exploring Zed, A Newly Open Source Code Editor Written In Rust

Exploring Zed, an open source code editor written in Rust

The Zed code editor sets itself apart with its lightning-fast performance and cutting-edge collaborative features.

Nefe Emadamerho-Atori
Apr 22, 2024 ⋅ 7 min read
Implementing Infinite Scroll In Next Js With Server Actions

Implementing infinite scroll in Next.js with Server Actions

Infinite scrolling in Next.js no longer requires external libraries — Server Actions let us fetch initial data directly on the server.

Rahul Chhodde
Apr 19, 2024 ⋅ 10 min read
Integrating Django Templates With React For Dynamic Webpages

Integrating Django templates with React for dynamic webpages

Create a dynamic demo blog site using Django and React to demonstrate Django’s server-side functionalities and React’s interactive UI.

Kayode Adeniyi
Apr 18, 2024 ⋅ 7 min read
View all posts

34 Replies to "Containerized development with NestJS and Docker"

  1. Thanks for the awesome article, it’s so interesting. Keep posting please.
    But I misunderstand the below statement ,
    “In the build config, we define the context, which tells Docker which files should be sent to the Docker daemon. In our case, that’s our whole application, and so we pass in ., which means all of the current directory.”

    Are you sure about this information ?

    In the official documentation it’s telling that context is “Either a path to a directory containing a Dockerfile, or a url to a git repository.”

    Thanks again.

  2. Hey Ghandri, thank you for your kind words. More Nestjs articles coming soon!
    Regarding context, here’s a quote from the documentation:

    “Either a path to a directory containing a Dockerfile, or a url to a git repository.

    When the value supplied is a relative path, it is interpreted as relative to the location of the Compose file. This directory is also the build context that is sent to the Docker daemon.”

    Hope that helps 🙂

  3. Thanks for the Article. Helped me a lot.
    What did not work for me was the –only=development part in Dockerfile. The process ends up with an error that packages are missing. When I removed the –only flag, everything works fine. Docker will only install dev dependencies what is not enough to debug the application, right?
    Also, where is the part how to run in production mode?

  4. Hey Patrick, glad you liked the article.

    Regarding the –only part, apps generated through the Nest CLI are using non-dev dependencies to build an application, which shouldn’t take place. Once the issue is resolved, the –only flag will work fine. I will adjust the article, thanks for catching it.

    The production mode is as simple as building an image like you would normally do with Docker. While I will admit that it could have been mentioned towards the end, the article assumes a basic understanding of Docker.

    https://docs.docker.com/get-started/part2/ – Here you can read about building and running Docker images. Hope that helps!

  5. HI Maciej, great article.
    So I just implemented as described above, however I’m running the production build I change both the target and the command instructions in the docker-compose.yml to production and npm run start:prod respectively but I keep getting the error, “Error: Cannot find module ‘/usr/src/app/dist/main'”. I don’t know if the same thing happened to you.
    I’ve tried debugging to no avail. Could you please help with this?

  6. This all works beautifully and the detailed instructions are very helpful. What is not working for me is the ‘watch’ mode from the Nest container to my local files. Even rebuilding the image with `docker-compose up –build -V -d` and starting from a new base image, which should refresh all the layer caches, is not dropping my file changes into the container. It had me confused for quite a while troubleshooting my database connection with mongo, when the real problem was unchanged files. What about this is not working/

    # image is provided by docker build of local app using Dockerfile
    container_name: nscc_nest
    restart: always
    context: .
    target: development
    – .:/usr/src/app

  7. I have fixed the above issue by looking at the author’s Github repo for another NestJS tutorial for websockets on this same blog (https://github.com/maciejcieslar/scalable-websocket-nestjs) and found something missing in the Dockerfile displayed above, which is the second line of `ENV NODE_ENV=development`. I’m not sure if this caused my containers to rebuild with the required copying of files, or some pruning that I did in my local Docker environment, but now the NestJS container picks up my code changes and runs in debug mode (still trying to hit a breakpoint).

    I have made a generic repo available for others to use as a starter on this tutorial (as some have requested), although I am using a MongoDB instance and Mongo-Express for web-based admin. It has some initial database seeding in it of users and documents. Check it out if you like. The Docker stuff works just as described here, only the databases have been changed to protect the innocent!


  8. If you encounter error during `docker-compose up` that rimraf and nest is not found add a command to Dockerfile before `RUN npm install –only=development`:
    RUN npm install -g rimraf @nestjs/cli

    Great tutorial BTW 🙂

  9. Nice article! Very helpful. Has anyone managed to get the the debugging to work in VSC? Debugger is attached but I am unable to set any breakpoints in the TS files. VSC gives me the error: Breakpoint ignored because generated code not found (source map problem?).

  10. This is a really great article. Thanks for taking the time to step through each piece and explain it thoroughly. I’m just getting started with Nestjs, but this article contained some great ideas that I’ll carry over to my other node projects.

  11. Yes, I had the same problem trying to run it with docker-compose. I had to update my launch.json to look like below, because my actual node project was nested one level inside of my workspace folder, and VS Code likes to put the .vscode/launch.json at the root of the workspace by default. So my folder structure is ~/Workspace/my-project-workspace/my-nest-app. My .vscode/launch.json file is at ~/Workspace/my-project-workspace, and I’m trying to map the source to files in the ~/Workspace/my-project-workspace/my-nest-app/dist

    “version”: “0.2.0”,
    “configurations”: [
    “type”: “node”,
    “request”: “attach”,
    “name”: “Debug: task-management”,
    “remoteRoot”: “/usr/src/app”,
    “localRoot”: “${workspaceFolder}/my-nest-app”,
    “protocol”: “inspector”,
    “port”: 9229,
    “restart”: true,
    “address”: “”,
    “outFiles”: [“${workspaceFolder}/my-nest-app/dist/**/*.js”],
    “skipFiles”: [“/**”],
    “sourceMaps”: true

    And my .tsconfig looks like this:
    “compilerOptions”: {
    “module”: “commonjs”,
    “declaration”: true,
    “removeComments”: true,
    “emitDecoratorMetadata”: true,
    “experimentalDecorators”: true,
    “target”: “es2017”,
    “sourceMap”: true,
    “outDir”: “./dist”,
    “baseUrl”: “./”,
    “incremental”: true
    “exclude”: [“node_modules”, “dist”]

  12. This was a great help! Can you please explain to me as a Docker newbie how you would deploy this app and use the production section of the Dockerfile? Would I have to use a different docker-compose.yml file with the build: target set as production? How else would this file change? Take out the volumes and command from main?

  13. You might need to do a build as the debugger is actually stepping through the .js files. It is a bit of an illusion when you put a breakpoint in .ts files, and once you change the code, the illusion breaks.

  14. Wow! This is one clear and thorough guide. Been a challenge to find clear guides such as this for devops related stuff for NestJs. Please keep it coming! 🔥}

    Just have a question, I currently am planning to build a web app using NestJS as the backend while also having Angular as the frontend. What is the best practice for this on Docker?

    Does that mean I will only need to create 2 new services; one being for Angular Development & Build which will build the angular app and copy the dist folder, and then two being the server that will serve this built app? (probably Nginx).

  15. Great tutorial Maciej! I’ve been trying to run npm run build with only devDependencies, and it’s not possible, because nest will build the app and therefore needs production deps. If we follow your tutorial (which is gorgeous btw) it works because you didn’t mention a .dockerignore and so it send all current directory as context to docker daemon. I’m just sharing my thoughts on this, if I’m doing something wrong or misunderstood something, please let me know.

  16. Hi Maciej!! Great article, that’s all I have to say. I have some Docker knowledge and be able to follow every step of the journey and agree with each line of code was a huge satisfaction.

    There’s only one line in the Dockerfile which I think is not necessary. In the second build where the “final” image is created, there’s a “COPY . .” that in my opinion is not necessary. You would be copying all the source files again into the final image, which are not needed and would bloat the final result.

    So, to be precise:

    // Dockerfile

    RUN npm install –only=production

    COPY . . # <– I think this instruction should be removed

    COPY –from=development /usr/src/app/dist ./dist

    The only caveats would be that:

    – The .env file won't be in the final image. This is not a problem because, as a best practice, environmental variables should be used instead or, if using docker-compose to test it, the .env file is loaded from outside the image, so it shouldn't be necessary to include it next to /dist folder.
    – Any other file that should be placed to connect and authenticate to external services (any kind of secret) must be copied explicitly to the final image.

    Again, thanks for the article, gratifying to find all the little pieces from some posts together in only one place.

    Keep it coming!

  17. This is a very good observation and thanks for sharing this. I was having the exact same problem until I noticed that indeed my .dockerignore file included “node_modules”. BTW, have you found a solution: removed node_modules from .dockeringore or run “npm install” without any flag.

  18. Thank you for the post, pretty good guide.

    I wonder if I’m making any mistake, because I got this error trying to run the docker container.

    $ docker run –name api -p 3000:3000 api:1
    throw err;

    Error: Cannot find module ‘/usr/src/app/dist/main’
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:797:15)
    at Function.Module._load (internal/modules/cjs/loader.js:690:27)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1047:10)
    at internal/main/run_main_module.js:17:11 {
    code: ‘MODULE_NOT_FOUND’,
    requireStack: []

  19. Ohhh man. Thank you very much for the detailed explanation. I’ve been struggling with this problem for about 2 weeks.

    After a short troubleshooting I was able to get my first Nest App working with Mongo using containers!

  20. This worked very well, but with node.Js 15, I get problems with user rights and other problems. Any idea what’s changed?

    1. I found what’s different and why it’s not working on my site with Node-apline >=15.
      I used in the docker-compose.yml
      command: npm run start:dev:docker
      which starts the script located in package.json
      “start:dev:docker”: “tsc-watch -p tsconfig.build.json –onSuccess \”node –inspect= dist/src/main.js\””
      with node:14-apline node is started as root user all works fine.
      But with >=15 now node is started as USER Node and not as root ! And this leads to the permission problem I had.
      Solution was now, not to call npm, instead I called direct in the docker-compose file
      command: “node /app/node_modules/tsc-watch/lib/tsc-watch -p tsconfig.build.json –onSuccess \”node –inspect= dist/src/main.js\””
      So again all is started as root user and works well.
      It looks to me, that npm is starting node no more as root in node-alpine>= 15.

  21. Hi! Thanks for the great article. It gives me a lot of info to start implementing a dockerized dev environment.
    I’m confused by the following step though:

    Here we install only devDependencies due to the container being used as a “builder” that takes all the necessary tools to build the application and later send a clean /dist folder to the production image.

    In my opinion it’s impossible to run a –only=development install and then run a build. It will always miss the main packages.

    RUN npm install –only=development
    COPY . .
    # this command will now miss e.g. nestjs/core package
    RUN npm run build

    I think I will need an intermediate stage in between my development and production stages to prune devDeps, like:

    FROM node:12.13-alpine As development

    RUN npm install
    COPY . ./
    RUN npm run build

    FROM node:12.13-alpine as build

    COPY –from=development . ./
    RUN npm prune –production

    FROM node:12.13-alpine as production

    COPY –from=build /dist /dist
    COPY –from=build /node_modules /node_modules

    Am I wrong?

  22. Awesome article! thanks a lot. This is my first time developing in a containerized project… this is great.
    Do you think developing this way could be slower than simply running the application directly on a developer’s own machine?

  23. This article is a true life-saver for making a docker-development container for your team, if this is your first time doing so. I burned an entire day and a half pulling my hair out as to why my dockerfile + compose file (with a single host-volume-mout) worked for everyone the first time they spun it up on Friday.. and then when I went to ‘docker compose up) on Monday .. I kept getting ‘sh: nest: command not found’ (it’s a nestJS app) .. npm install .. node –version.. everything worked fine except for the exact same command that worked on Friday.. and I had no clue.. NOTHING HAD CHANGED !! … My first clue was commenting out the volumes section of the compose file which brought it back to a working state.. and then I had better context for what to search for, and found a few articles, but none as clear and concise as this one.
    Thank you!!

Leave a Reply