Debugging NodeJS in Docker

Describing how I debug a local NodeJS & TypeScript service when developing

Posted by Eddo on June 16, 2020

Since a month or two I’ve switched teams, and now implementing features in NodeJS and a React app. As easy as debugging a PHP application can be, I have seen some difficulty in getting debugging running properly for a NodeJS service. With this article I’d like to elaborate on how I’ve achieved a better way than using console.log(var) statements.

The NodeJS service I’m referring to is a single microservice that acts as part of a larger API. It is written in TypeScript, and has dependencies on other microservices. For local development it is running in Docker and I’m using the other services in the staging environment. The dependency of a database is handled by a second Docker container.

Orchestrating your local application

To create my local environment, I’ve created a docker-compose.yml file that starts the MySQL database and the app.

version: '3.4'

    image: mysql:5.7
      MYSQL_DATABASE: nodejs-dev
    restart: always
      - db-data:/var/lib/mysql
      - 3306:3306

      context: .
      dockerfile: Dockerfile
      target: base
      DB_HOST: mysql2://root:@mysql:3306/
      DB_NAME: nodejs-dev
      LOG_LEVEL: debug
      NODE_ENV: develop
      - .:/usr/src/app
      - /usr/src/app/node_modules # always use the container version of `node_modules` folder
      - 3000:80
      - 9229:9229
    restart: on-failure
    # Don't start npm before the MySQL db is ready
    command: ./ mysql:3306 -- npm run debug
      - mysql

    driver: local

Before I joined the team, the application was already deployed as a Docker container. Initialy I created a second Dockerfile for local development, but quickly found on Stackoverflow that as of a docker-compose version 3.4 I could target a specific build stage to run the app locally. This negated the need of a second Dockerfile.

# ---- Base ----
FROM node:12 AS base

ENV HOME=/usr/src/app

# 'netcat' is equired by ''
RUN apt-get -q update && apt-get -qy install netcat

COPY ./package.json $HOME/
COPY ./package-lock.json $HOME/
RUN npm ci --log-level=error

# ---- Deps ----
FROM base AS deps

RUN npm prune --only=prod --log-level=error

# ---- Build ----
FROM base AS build

RUN npm run build

# ---- Release ----
FROM node:12-alpine

ENV HOME=/usr/src/app

COPY --from=deps $HOME $HOME
COPY --from=build $HOME/build $HOME/build


CMD ["npm", "start"]

In order to get the application running I now only have to type docker-compose up in my command line. This will build the app for me, if not already done, and make sure I can connect to it locally. As the NodeJS app depends on the MySQL database to be running, I’m using a wait-for script to defer starting NodeJS until the MySQL db is ready.


To allow debugging there are a few steps needed. First thing is to ensure the TypeScript files are compiled with source maps enabled. Otherwise you need to debug the compiled files instead of the original TypeScript files. I’ve enabled that by adding a builddev script as npm run command in package.json:

"scripts": {
    "builddev": "./node_modules/.bin/babel src --out-dir build --extensions '.ts' --copy-files --source-maps",
    "predebug": "npm run builddev && npm run migrate",
    "debug": "nodemon --watch src --inspect= build/index.js"

The app itself is started with npm run debug in the local Docker container which enables the use of the NodeJS inspector. The debugger of your IDE should then be able to connect to it. This allows you to step through your code upon execution.

💡Run docker-compose run --rm app npm run watch if you want to continuously build the Typescript. The other, already running, container will take the built files and restart itself. General idea is that I don’t want to run any npm scripts locally, only in Docker.

The second step is to configure your IDE. I’m using Visual Studio Code and have added the following launch configuration to connect to my local NodeJS app in Docker:

  "name": "Docker: Attach to Node",
  "type": "node",
  "request": "attach",
  "port": 9229,
  "address": "localhost",
  "cwd": "${workspaceFolder}",
  "localRoot": "${workspaceFolder}",
  "remoteRoot": "/usr/src/app",
  "outFiles": [ "${workspaceRoot}/build/**/*.js" ],
  "sourceMaps": true,
  "protocol": "inspector"

I can now use breakpoints that I set in my TypeScript code when calling the local NodeJS service 💪🏻.

There is now one thing that I still don’t have fixed, that is running mocha tests within a Docker container. I still need to run npm locally for that to work unfortunately. Something for another day to resolve.


P.S. If you’ve enjoyed this article or found it helpful, please share it, or check out my other articles. I’m on Instagram and Twitter too if you’d like to follow along on my adventures and other writings, or comment on the article.