This time we want to enable line by line debugging of our microservice inside a running container. Normally I would advise to first do the debugging while running locally and only use this method as last resort, if something really does not work while running inside a container and apparently works when running the microservice directly on the host.
As always we want to provide an experience as friction free as possible. Thus we will once more use our beloved tool docker-compose
.
As usual, the code accompanying this post can be found on GitHub. You can read the two previous posts of this series here and here.
Using a code editor such as VS Code it is easy to debug a .NET Core application that is running natively on the host. But that is not the point of this post. We want to run our microservice inside a container and debug inside there. For this we need to install a debugger in the container where the microservice will be running. Since the debugger shall never be part of the final Docker image that will be used in production we have to create a special debug image that will be exclusively used in case we need to line-by-line debug our service.
Creating a special Docker Image for Debugging
Let’s thus start crafting that Dockerfile. We will call this Dockerfile Dockerfile-debug
to distinguish it from the default one. We can (and should) use the default Dockerfile as a template for this special one. Here is my version:
Create a file Dockerfile-debug
in the api
folder and add the above code snippet to it.
On line 1 of the Dockerfile we are using the same base image as in our standard image. Then in the run command on lines 2 through 4 we use the Linux package manager apt
to install the zip
tool which we will then use on line 4 to decompress the debugger vsdbg
that we download from Microsoft (https://aka.ms/getdbgsh
).
Subsequently we define the working directory inside the container (line 5), copy the content of the publish
folder from the host to the app
folder in the container (line 6) and define the container start command (line 7).
With this approach we are relying on the fact that the application or microservice has been published on the host first, using dotnet publish
.
Authoring a Docker Compose File for Debugging
Usually we want to debug the microservice when it is running together with its external dependencies such as the database. Thus we will now craft a special Docker Compose file that will allow us to run the service and the Postgres DB that we have used in all our previous examples. Add a file called docker-compose-debug.yml
to the project folder demo-project
. To this file add the following code snippet:
We do not have to discuss lines 1 through 13 since that is unchanged compared to the other Docker Compose files we used so far. The interesting part is on lines 15 through 19. On line 16 we define an image name for this special debugging case (acme/api-debug:1.0
). On lines 17 through 19 we define how the image shall be built when running this application. We are providing the context (subfolder api
) and the name of the Dockerfile to use (which must be part of the context, that is, in our case inside the api
folder. More is not needed at this time.
Let’s run the application with the command:
docker-compose -f docker-compose-debug.yml up --build
Notice the --build
argument which instructs Docker Compose to build any image needed before running. In our case this is the image for the api
service.
So far this was the easy part. Now we need to configure our editor VS Code to execute debugger in the api
service container. We also need to tell the debugger to which process running inside the container it should attach. We can do this by defining a launch configuration in the launch.json file residing in subfolder .vscode
.
Configuring VS Code for Debugging
Open VS Code from within the project folder demo-project
. Create a subfolder .vscode
in the project folder demo-project
(if it doesn’t already exist) and add a file launch.json
to it. Add the following code snippet to it:
On line 5 we define the name of the launch configuration that will appear in the drop down on the debug view of VS Code. The type of the configuration is coreclr
(line 6) and we want to attach
(to a process; line 7). The process we want to have launched in the pickRemoteProcess
. This will open a drop down in VS Code when you start debugging showing a list of available processes. But we need to specify what processes we want to see, and that is done via the pipeTransport
part of the configuration. We want the pick process command to use the following command to run the debugger inside the container:
docker-compose -f docker-compose-debug.yml \
exec -T api /vsdbg/vsdbg
This is a combination of the information found on lines 10 through 12.
In VS Code switch to the debugging view, make sure you have the configuration .NET Code Docker Attach (Compose)
selected and then hit the green run button:

api
containeras soon as you have clicked the debug run button the the process selection popup will open and present the list of all processes running inside the api
container:

Select the process dotnet api.dll
as the one the debugger shall attach to. You will see output generated in the DEBUG CONSOLE
tab of VS Code:

Add a break point e.g. on line 17 of the ValuesController
of the api
microservice. Use curl
to access the API of the microservice:
curl http://localhost:5000/api/values
and observe how the debugger stops at the break point. You can now analyze variables and watches as well as the call stack. You can also start walking through the code line by line.
Summary
In this 3rd post of the series A Docker Workflow for .NET Developers I have shown how we can use Docker Compose to run a microservice and its dependencies such as a database, where the microservice is ready to be debugged inside its container. Our code editor VS Code can then be configured to launch the debugger in the microservices’ container and attach it to the correct process. Once the debugger is attached we can line-by-line debug the microservice exactly the same way as if it would run natively on the host machine.
The code accompanying this series can be found on GitHub.