Debugging Docker Compose Solutions in JetBrains Rider: A Deep Dive
Tags: dotnet, docker, microservices, webdev
Published: 2023-09-08 13:20:27

## Introduction

If you've ever used JetBrains Rider's built-in feature for debugging Docker and Docker Compose solutions, you may have found it works like a charm — or maybe not. I encountered issues with this feature where the debugger just wouldn't kick off. Unable to discern a pattern, I decided to investigate how Rider handles Docker Compose debugging under the hood. In this article, we'll walk through the setup, explore the commands Rider uses, dissect the generated Docker Compose file, and learn how to troubleshoot common issues.

## Setting Up a Simple Web App Project

To understand how Rider's debugging works, I started by creating a basic C# web application. Below is the Dockerfile for the application.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3dfjwo2cycy66bqkt0v.png)


```Dockerfile
# Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443


FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["Service1/Service1.csproj", "Service1/"]
RUN dotnet restore "Service1/Service1.csproj"
COPY . .
WORKDIR "/src/Service1"
RUN dotnet build "Service1.csproj" -c Release -o /app/build


FROM build AS publish
RUN dotnet publish "Service1.csproj" -c Release -o /app/publish /p:UseAppHost=false


FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Service1.dll"]
```

## The Docker Compose Configuration

I also created a `docker-compose.yml` file and an override file, `docker-compose.override.yml`, to manage the services.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l26jpnpdtphcl9hzw87o.png)


```yml
# docker-compose.yml
version: '3.8'

services: 
 service1:
   image: service1
   build:
     context: .
     dockerfile: ./Service1/Dockerfile


 service2:
   image: service2
   build:
     context: .
     dockerfile: ./Service2/Dockerfile
```

```yml
# docker-compose.override.yml
version: '3.8'

services:
 service1:
   ports:
     - 8081:80


 service2:
   ports:
     - 8082:80
```

## Initiating Debugging in Rider

After configuring the Docker Compose setup in Rider, I clicked the "Debug" icon to initiate the debugging process.

## Understanding the Command Rider Uses

Rider's approach diverges from what one might expect, which is the `docker-compose up -f` command. If you take a look at the Service/Console window, you'll see the command Rider actually uses:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4y8zvvhs9dva70rnmpue.png)


```bash
/usr/local/bin/docker compose -f /path/to/docker-compose.yml -f /path/to/docker-compose.override.yml -f /path/to/docker-compose.generated.override.yml -p project up -d
```

Note: I am using a Mac, so the file paths might differ on a Windows system.

## Analyzing the Generated Docker Compose File

Rider generates an additional Docker Compose override file that provides crucial settings for debugging. Here's a simplified snippet:

```yml
# This is a generated file. Not intended for manual editing.
version: "3.8"
services:
 service1:
   build:
     context: "/Users/marshinov/Work/tools/DockerDebug"
     dockerfile: "./Service1/Dockerfile"
     target: "base"
   command: []
   entrypoint:
   - "/riderDebugger/linux-arm64/dotnet/dotnet"
   - "/riderDebugger/JetBrains.Debugger.Worker.exe"
   - "--mode=server"
   - "--frontend-port=57100"
   - "--backend-port=57300"
   environment:
     ASPNETCORE_ENVIRONMENT: "Development"
     DOTNET_USE_POLLING_FILE_WATCHER: "true"
     NUGET_PACKAGES: "/Users/marshinov/.nuget/packages"
     NUGET_FALLBACK_PACKAGES: "/Users/marshinov/.nuget/packages"
     RIDER_DEBUGGER_LOG_DIR: "/riderLogs"
     RESHARPER_LOG_CONF: "/riderLogsConf/backend-log.xml"
   image: "service1:dev"
   ports:
   - "127.0.0.1:57002:57100"
   - "127.0.0.1:57202:57300"
   volumes:
   - "/Users/marshinov/.nuget/packages:/root/.nuget/fallbackpackages"
   - "/Users/marshinov/Work/tools/DockerDebug/Service1:/app:rw"
   - "/Users/marshinov/Work/tools/DockerDebug:/src:rw"
   - "/Users/marshinov/.local/share/JetBrains/RiderRemoteDebugger/2023.2.1/LinuxArm64:/riderDebugger"
   - "/Users/marshinov/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-0/232.9559.61/Rider.app/Contents/bin:/riderLogsConf"
   - "/Users/marshinov/Library/Logs/JetBrains/Rider2023.2/DebuggerWorker/JetBrains.Debugger.Worker.2023_9_06_12_27_11:/riderLogs:rw"
   working_dir: "/app"
 service2:
   build:
     context: "/Users/marshinov/Work/tools/DockerDebug"
     dockerfile: "./Service2/Dockerfile"
     target: "base"
   command: []
   entrypoint:
   - "/riderDebugger/linux-arm64/dotnet/dotnet"
   - "/riderDebugger/JetBrains.Debugger.Worker.exe"
   - "--mode=server"
   - "--frontend-port=57100"
   - "--backend-port=57300"
   environment:
     ASPNETCORE_ENVIRONMENT: "Development"
     DOTNET_USE_POLLING_FILE_WATCHER: "true"
     NUGET_PACKAGES: "/Users/marshinov/.nuget/packages"
     NUGET_FALLBACK_PACKAGES: "/Users/marshinov/.nuget/packages"
     RIDER_DEBUGGER_LOG_DIR: "/riderLogs"
     RESHARPER_LOG_CONF: "/riderLogsConf/backend-log.xml"
   image: "service2:dev"
   ports:
   - "127.0.0.1:57003:57100"
   - "127.0.0.1:57203:57300"
   volumes:
   - "/Users/marshinov/.nuget/packages:/root/.nuget/fallbackpackages"
   - "/Users/marshinov/Work/tools/DockerDebug/Service2:/app:rw"
   - "/Users/marshinov/Work/tools/DockerDebug:/src:rw"
   - "/Users/marshinov/.local/share/JetBrains/RiderRemoteDebugger/2023.2.1/LinuxArm64:/riderDebugger"
   - "/Users/marshinov/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-0/232.9559.61/Rider.app/Contents/bin:/riderLogsConf"
   - "/Users/marshinov/Library/Logs/JetBrains/Rider2023.2/DebuggerWorker/JetBrains.Debugger.Worker.2023_9_06_12_27_11:/riderLogs:rw"
   working_dir: "/app"

```



This generated file performs several tasks:
- Attaches the Rider Remote debugger
- Overrides the original entry point to use the Rider Remote Debugger
- Ignores DLLs in the original image and uses source code mounted via volumes
- Opens additional ports for debugging

These changes are transformative, so much so that Rider essentially creates a completely different runtime environment, optimized for debugging.

## Troubleshooting: The Issue I Faced

In my case, Rider ignored environment variables defined in my `.env` file, which are usually considered by the `docker-compose up -d` command. The `COMPOSE_PROJECT_NAME` variable, in particular, led to confusion when toggling between standard Docker commands and Rider-based debugging. To resolve this, I had to explicitly specify the project name in Rider's Docker Compose settings.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hxgtbebn3xvh3zfnwb6l.png)

## Conclusion

After digging deep into how JetBrains Rider handles Docker Compose debugging, I've found a reliable workaround for the issues I was facing. Rider performs several substantial changes to your Docker Compose configurations to enable debugging, so understanding these can be key to troubleshooting effectively. I hope this deep dive saves you some time and helps you debug your applications more efficiently. Happy coding!

P.S. Visual Studio does a [similar thing](https://goatreview.com/understand-how-visual-studio-integrates-debugging-in-docker-compose/).