Customize container images for debugging

Note

This section describes how you can customize your Docker containers when you choose the Dockerfile container build type. If you are using the .NET SDK build type, the customization options are different, and most of the information in this section isn't applicable. Instead, see Containerize a .NET app with dotnet publish.

When you build in the Debug configuration, there are several optimizations that Visual Studio does that help with the performance of the build process for containerized projects. The build process for containerized apps isn't as straightforward as simply following the steps outlined in the Dockerfile. Building in a container is slower than building on the local machine. So, when you build in the Debug configuration, Visual Studio actually builds your projects on the local machine, and then shares the output folder to the container using volume mounting. A build with this optimization enabled is called a Fast mode build.

In Fast mode, Visual Studio calls docker build with an argument that tells Docker to build only the first stage in the Dockerfile (normally the base stage). You can change that by setting the MSBuild property, DockerfileFastModeStage, described at Container Tools MSBuild properties. Visual Studio handles the rest of the process without regard to the contents of the Dockerfile. So, when you modify your Dockerfile, such as to customize the container environment or install additional dependencies, you should put your modifications in the first stage. Any custom steps placed in the Dockerfile's build, publish, or final stages aren't executed.

This performance optimization normally only occurs when you build in the Debug configuration. In the Release configuration, the build occurs in the container as specified in the Dockerfile. You can enable this behavior for the Release configuration by setting ContainerDevelopmentMode to Fast in the project file:

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
   <ContainerDevelopmentMode>Fast</ContainerDevelopmentMode>
</PropertyGroup>

If you want to disable the performance optimization for all configurations, and build as the Dockerfile specifies, then set the ContainerDevelopmentMode property to Regular in the project file as follows:

<PropertyGroup>
   <ContainerDevelopmentMode>Regular</ContainerDevelopmentMode>
</PropertyGroup>

To restore the performance optimization, remove the property from the project file.

When you start debugging (F5), a previously started container is reused, if possible. If you don't want to reuse the previous container, you can use Rebuild or Clean commands in Visual Studio to force Visual Studio to use a fresh container.

The process of running the debugger depends on the type of project and container operating system:

Scenario Debugger process
.NET Core apps (Linux containers) Visual Studio downloads vsdbg and maps it to the container, then it gets called with your program and arguments (that is, dotnet webapp.dll), and then debugger attaches to the process.
.NET Core apps (Windows containers) Visual Studio uses onecoremsvsmon and maps it to the container, runs it as the entry point and then Visual Studio connects to it and attaches to the program.
.NET Framework apps Visual Studio uses msvsmon and maps it to the container, runs it as part of the entry point where Visual Studio can connect to it, and attaches to the program. This is similar to how you would normally set up remote debugging on another computer or virtual machine.

For information on vsdbg.exe, see Offroad debugging of .NET Core on Linux and OS X from Visual Studio.

Modify container image for debugging and production

To modify the container image for both debugging and production, modify the base stage. Add your customizations to the Dockerfile in the base stage section, usually the first section in the Dockerfile. Refer to the Dockerfile reference in the Docker documentation for information about Dockerfile commands.

# This stage is used when running from VS in fast mode (Default for Debug configuration)
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# <add your commands here>

# This stage is used to build the service project
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["WebApplication3/WebApplication3.csproj", "WebApplication3/"]
RUN dotnet restore "WebApplication3/WebApplication3.csproj"
COPY . .
WORKDIR "/src/WebApplication3"
RUN dotnet build "WebApplication3.csproj" -c Release -o /app/build

# This stage is used to publish the service project to be copied to the final stage
FROM build AS publish
RUN dotnet publish "WebApplication3.csproj" -c Release -o /app/publish

# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication3.dll"]

Modify container image only for debugging

You can customize your containers in certain ways to help in debugging, such as installing something for diagnostic purposes, without affecting production builds.

To modify the container only for debugging, create a stage and then use the MSBuild property DockerfileFastModeStage to tell Visual Studio to use your customized stage when debugging. Refer to the Dockerfile reference in the Docker documentation for information about Dockerfile commands.

Note

The instructions here apply to the single-container case. You can also do the same thing for multiple containers with Docker Compose, but the techniques required for Docker Compose are slightly different. For example, the stage is controlled by a setting in the dockercompose.debug.yml file.

In the following example, we install the package procps-ng, but only in debug mode. This package supplies the command pidof, which Visual Studio requires (when targeting .NET 5 and earlier) but isn't in the Mariner image used here. The stage we use for fast mode debugging is debug, a custom stage defined here. The fast mode stage doesn't need to inherit from the build or publish stage, it can inherit directly from the base stage, because Visual Studio mounts a volume that contains everything needed to run the app, as described earlier in this article.

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

# This stage is used when running from VS in fast mode (Default for Debug configuration)
FROM mcr.microsoft.com/dotnet/aspnet:6.0-cbl-mariner2.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM base AS debug
RUN tdnf install procps-ng -y

# This stage is used to build the service project
FROM mcr.microsoft.com/dotnet/sdk:6.0-cbl-mariner2.0 AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build

# This stage is used to publish the service project to be copied to the final stage
FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish

# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]

In the project file, add this setting to tell Visual Studio to use your custom stage debug when debugging.

  <PropertyGroup>
     <!-- other property settings -->
     <DockerfileFastModeStage>debug</DockerfileFastModeStage>
  </PropertyGroup>

Customize debugging images with AOT deployment

To support native AOT deployment, the GNU debugger (GDB) is installed, but only on the image used when debugging, not the final runtime image. The Dockerfile includes a build argument LAUNCHING_FROM_VS which can be true or false. If true, the aotdebug stage is used, which is where GDB is installed. Note that Visual Studio only supports native AOT and GDB for Linux containers.

# These ARGs allow for swapping out the base used to make the final image when debugging from VS
ARG LAUNCHING_FROM_VS
# This sets the base image for final, but only if LAUNCHING_FROM_VS has been defined
ARG FINAL_BASE_IMAGE=${LAUNCHING_FROM_VS:+aotdebug}

# ... (other stages omitted)

# This stage is used as the base for the final stage when launching from VS to support debugging in regular mode (Default when not using the Debug configuration)
FROM base as aotdebug
USER root
# Install GDB to support native debugging
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
    gdb
USER app

# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration)
FROM ${FINAL_BASE_IMAGE:-mcr.microsoft.com/dotnet/runtime-deps:8.0} AS final
WORKDIR /app
EXPOSE 8080
COPY --from=publish /app/publish .
ENTRYPOINT ["./WebApplication1"]

You can use aotstage in the Dockerfile to customize the image used at debug time, without affecting the final image used when not launching from Visual Studio, or in production. For example, you could install a tool for use only during debugging.