Skip to content

How to Debug NodeJS App Running Inside Docker Container?

The goal of this example is to debug a Node.js application/server running inside a docker container. The guide is intended for development, and not for production deployment. The guide also assumes you have a basic understanding of Docker and Nodejs. And you have a docker-compose project already set up.

Now, why should we use the debugger to debug the app, or why to debug inside the container in the first place?

Most of the time you can be well off running your app on your local machine and use containers only to sandbox your databases and messaging queues, but some bugs will show themselves only when the app/server itself is containerized as well. Whereas, using a logger inside the containerized app to log variables and debug takes time and requires you to restart after adding every log, and generally it’s not that effective. In these cases, it is very helpful to know how to attach a debugger to the service. But, it is often difficult to configure. We will follow a simple way which will require you to set up one time only.

Now, Before we get started I want you to first know how we are going to achieve this. We are going to use NodeJS inspector to run a debugger on a different port than the one we are going to run the server on. This debugger port will be given to the VSCode debugger so it can make API calls to it. Meanwhile, our actual ExpressJS server will be running on another process and we will make API calls to that from the client. We will be able to add breakpoints and debug our code.

Set Debug Mode Ports

Let’s start by setting up a port for debug mode as below in your project .env file


MYAPP_PORT=8080
DEBUG_MODE_PORT=9229

And now pass and map this port in the docker-compose.yml file like below, also pass a DEBUG_MODE and SERVICE_NAME env variables like below. We will use these two later.


version: "3.8"
services:
  myapp:
    build: myapp/
    container_name: myapp
    ports:
      - $MYAPP_PORT:$MYAPP_PORT
      - $DEBUG_MODE_PORT:$DEBUG_MODE_PORT
    environment:
      - SERVICE_NAME=myapp
      - PORT=$MYAPP_PORT
      - DEBUG_MODE=$DEBUG_MODE
      - DEBUG_MODE_PORT=$DEBUG_MODE_PORT

Keep in mind I have named my service in this example as myapp you should pass what your service name is. You might have noticed that we haven’t set DEBUG_MODE var in the .env file, that’s because we will set it when running our containers.

Set Debug Script

We will be using node inspect so you must be familiarized with its commands. Now we will add a debug script inside application package.json as below. I have passed index.js as my entry point file set whatever yours is.


{
  "name": "debug_nodejs_service_example",
  "version": "1.0.0",
  "description": "example project of how to debug a nodejs application running inside docker container",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "debug": "node --inspect=0.0.0.0:$DEBUG_MODE_PORT --nolazy index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Asad Ullah Aziz",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.2"
  }
}

Set Entrypoint Bash Script

Create a docker-entrypoint.sh and write the below script.


#!/bin/bash

# Check if DEBUG_MODE is set
if [ "$DEBUG_MODE" = "$SERVICE_NAME" ]; then
    #  Run Node.js app in debug mode
    echo "Starting server in debug mode"
    npm run debug
else
    # Run Node.js noramlly
    echo "Starting server"
    npm start
fi

Remove any CMD command for starting the server from your service Dockerfile. For me I removed the following


CMD [ "npm", "start" ]

And now my Dockerfile looks like this. Yours might look different based on your requirements.


FROM node:18-alpine3.18

# Create app directory
WORKDIR /usr/app

# Install app dependecies
COPY package.json .
RUN npm install

# Bundle app source
COPY . .

And now give the following command to your service in your project docker-compose.yml file like below. Keep in mind the path to the docker-entrypoint.sh file is relative to the path you provided in the build i.e service Dockerfile


version: "3.8"

services:
  myapp:
    build: myapp/
    container_name: myapp
    command: sh ./docker-entrypoint.sh
    ports:
      - $MYAPP_PORT:$MYAPP_PORT
      - $DEBUG_MODE_PORT:$DEBUG_MODE_PORT
    environment:
      - SERVICE_NAME=myapp
      - PORT=$MYAPP_PORT
      - DEBUG_MODE=$DEBUG_MODE
      - DEBUG_MODE_PORT=$DEBUG_MODE_PORT

Set Debugger Launch File

Create a custom debug launch file for your service by clicking on create a launch.json file. And selecting Nodejs from the dropdown menu. Inside the Run and Debug panel inside Visual Studio Code.

Replace the default config with the following.


{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "attach",
            "name": "myapp",
            "port": 9229, // Set this port to what you did in DEBUG_MODE_PORT in .env file
            "restart": true,
            "localRoot": "${workspaceFolder}\\myapp\\index.js", // This should be the path to your server entrypoint file relative to the vscode workspace folder
            "remoteRoot": "/usr/app" // set this to what you set your WORKDIR in service Dockerfile
        }
    ]
}

Debugging Time

Now finally we can run our node service in debug mode using the following command.


 $ DEBUG_MODE=myapp docker compose up -d --build

And Voila!

Now we can attach the VSCode debugger to this port. Go to the debugger section, add any breakpoints to your code and select the application for which you added launch.json and click the green play button.

Once the debugger is attached and breakpoints are added you can now hit the API using the browser or Postman and see the code stop at the breakpoint as below. You can inspect variables live and follow along with changes in code.

I hope this tutorial helped you set up a debugger for your Nodejs microservice. You can get the source code for this example here.

More like this:

Multi-Source DB Locking: Concurrency & Data Integrity in Databases

Multi-Source DB Locking: Concurrency & Data Integrity in Databases

In the realm of database technology, even the most basic actions, such as checking your balance or receiving…
How to Set Up Google OAuth 2.0 Using Passport.js in MERN?

How to Set Up Google OAuth 2.0 Using Passport.js in MERN?

Welcome to this comprehensive tutorial, where we’ll embark on an exciting journey of integrating Gmail authentication seamlessly into…
Empowering Microservice Architecture with Ambassador and Kafka

Empowering Microservice Architecture with Ambassador and Kafka

In recent years, microservice architecture has emerged as a popular approach for building scalable, flexible, and resilient software…