👈 Kevin B. Ridgway

Debugging Node.js in Docker with Visual Studio Code

Tags: writing, javascript, docker, debugging, visual studio code,

February 10, 2018

I ran into this recently, where I really wanted to dig into a Dockerized Node app that I’d been given. I didn’t know a great deal about how the code executed, and wanted to step through it to understand it. It was a long-running ETL process that required a lot of knowledge about the data, as well as how the data was transformed to correctly make changes to the app.

I knew that you could debug per usual when inside a Docker container, as long as you exposed the ports, but wasn’t sure exactly how given that (1) Node had changed how it debugs recently, and (2) since I was using Visual Studio Code day-to-day at this point, I wanted that really nice debugging experience as if the Node app was running locally.

I searched around and since Node and VSCode had changed a lot in the last year or so everything I found in my searches for how to do it right was slightly off, or not working. Once I figured it out, I wanted to remember and share what I learned.

What Version of Node.js?

You need to know this before you start. And in the case of debugging in a Docker container, it should be straight forward, by looking at the first line of your Dockerfile, you should see what version of Node.js you’re using especially if you’re using the official Docker images, e.g. (FROM node:4.2.3).

The reason you need to identify what version of Node you’re running is because the way you initiate debugging, and the ports that the debugging protocol communicates over has changed. The reason for this is because Node updated the underlying runtime V8 that it uses, which the folks who work on the the Chrome V8 Runtime

For recent versions of Node, you can refer to the Node debugging docs. Looking at the documentation for previous versions it looks like it switch from the old way from debug to inspect at version v8.9.4.

For Node versions v8.9.4+, you have two options when starting your Node app:

  • inspect
    
* ```bash
inspect-brk

These run on port 9229 by default, but you can change that if you wish.

For Node versions v7.10.1 and earlier, your options are:

  • debug
    
* ```bash
debug-brk

These run on port 5858 by default, and you cannot change that.

Note: inspect was available here, but was experimental, so don’t know how stable it would be on these versions. I, for one, would not want an unstable debugging API when trying to debug the thing I really want to look at, so I wouldn’t recommend it.

So for your app running on Node in a Docker container you need to know two things to do it successfully:

  1. The keyword you pass to Node in the CMD of your Dockerfile (or script if you call one)
  2. The port on which you need to debug, and therefore EXPOSE on your Docker container, so your local machine can reach your Node app inside the container

V8 Inspector Integration for Node.js

V8 Inspector integration allows attaching Chrome DevTools to Node.js instances for debugging and profiling. It uses the Chrome Debugging Protocol.

Also you can supply a custom port:

--inspect=9222

The main difference here is you get the joy of debugging in Chrome Dev Tools, built into Node, a huge win. And you need to know the difference is this is a flag, rather than a keyword. In other words, make sure you’re passing the -- or not, and that this changes how you’re debugging. Those two dashes means wondering why you’re debugging on the commandline versus debugging with Chrome Dev Tools. A big gotcha.

One other note, in the v7.x docs of Node, you were able to send the Unix signal SIGUSR1 to make Node jump into debugging mode, but was deprecated in favor of the V8 inspector (as an experimental feature in that version of v7.x). You may run into older articles out there on the web that mention this method, and yes that works, but only if it’s an early version of Node — not in later ones.

The Docker Files

# For Node versions v8.9.4+:
FROM node:8.9.4

EXPOSE 3000 # what port your app runs on
EXPOSE 9222 # what port you need to debug on
COPY . /app
WORKDIR /app

RUN cd /app; npm install
CMD ["node", "--debug=9222","index.js"]
# For Node versions v7.10.1 and earlier:
FROM node:7.0.0

EXPOSE 3000 # what port your app runs on
EXPOSE 5858 # what port you need to debug on
COPY . /app
WORKDIR /app

RUN cd /app; npm install
CMD ["node", "--debug=5858","index.js"]

Build your container:

docker build -t 'nodeapp' .

Run your container:

docker run -d nodeapp

You should see something like this, (with a different GUID at the end of the url), if you have a later version of Node and started with the --inspect-brk flag, which is usually what I do:

Debugger listening on ws://0.0.0.0:5858/ac9852b2-9e7b-4064-9969-ca3f8174cfee
For help see https://nodejs.org/en/docs/inspector
Debugger attached.

Debugging With Visual Studio Code

Now that we have your Node app waiting in a debugger mode, it’s time to attach to it! (These instructions assume Version 1.20.0 of Visual Studio Code as of early 2018. Your mileage may vary depending on when you’re reading this.)

  1. First get to the debugging pane in Visual Studio on your Mac or Windows computer:

    • Mac: SHIFT + CMD + D
    • Windows: SHIFT + WIN + D
  2. Click on the dropdown menu to the right of DEBUG in the upper left-hand corner of Visual Studio code.

  3. Choose Add Configuration

  4. Choose Node

  5. When the launch.json file opens up and will give you another list to choose from and you should choose Docker: Attach to Node

  6. Save the launch.json file.

  7. You should now see Docker: Attach to Node in that first dropdown in step #2. Choose it.

A Short Detour

Your settings in launch.json must match up with how your app is configured in directory structure, etc.

Notice how I changed the remote root so it matches what was in our Dockerfile. In this example below I’m debugging the v8.9.4 Node app.

{
  // 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": "Docker: Attach to Node",
      "port": 9229,
      "address": "localhost",
      "localRoot": "${workspaceFolder}",
      "remoteRoot": "/app/",
      "protocol": "inspector"
    },
    {
      "name": "Docker: Attach to Node",
      "type": "node",
      "request": "attach",
      "port": 9229,
      "address": "localhost",
      "localRoot": "${workspaceFolder}/",
      "remoteRoot": "/app/",
      "protocol": "inspector"
    }
  ]
}
  1. Now click the green triangle to the left of it to initiate debugging from Visual Studio Code.
  2. You should see Visual Studio Code open the file with the first line of your Node program, and have it highlighted!

You can now set breakpoints, and hit pause, step over, step in/out, restart, and stop, in the little controls above the code editor window. For more details checkout Visual Studio Code’s debugging docs. It is a great IDE for Node.


Kevin B. Ridgway
  • Hi, I'm a Senior Software Engineer living in sunny 🌨 Buffalo!
  • I'm an 🕸 Interweb & 🚀 Aerospace Enthusiast.
  • I like to build 👨🏼‍💻 and design 👨‍🎨 things. A lot.
  • I speak in 🦄 🌈 🎉 emojis and gifs 📷 sometimes all the time.
  • 😜🤳
Office Guys Dancing Hard