Docker Build Debugging in VS Code

19 Apr
DockerVSCode

Docker Build Debugging in VS Code

Debugging a Docker build has always had an awkward side to it. When a Dockerfile build fails, it rarely fails in a friendly place. Most of the time you end up staring at an unhelpful error line, rereading the file, rebuilding the image, and trying to guess what state the image was left in.

To address that pain, Docker brought this experience to VS Code through Docker DX. It lets you stop the build at the right step, inspect variables, explore the filesystem, and even open a temporary shell to test commands before changing the Dockerfile.

This is specifically about build debugging in VS Code, not about attaching a debugger to an application that is already running inside a container.

What Docker build debugging looked like before Docker DX

Before this extension, the flow usually looked like this:

  1. run docker build;
  2. read an error message that is not always very helpful;
  3. comment out lines after the failure point;
  4. build again;
  5. start an intermediate container with docker run;
  6. open a shell and test commands manually;
  7. go back to the Dockerfile, fix it, and repeat.

The problem was not only the time you lost. It was also the lack of context. Instead of watching the build happen step by step, you ended up experimenting around it and rebuilding the image state in your head.

When the problem was a COPY, a variable, a bad WORKDIR, or just a typo, that loop got old very quickly.

What changed with Docker DX

Docker DX is Docker's official extension for VS Code. The most relevant part here is build debugging, built on top of Buildx and the Debug Adapter Protocol, or DAP.

In practice, that gives you the ability to:

  • set breakpoints on Dockerfile instructions;
  • step through the build line by line;
  • inspect ARG and ENV variables;
  • explore the filesystem at that stage of the build;
  • open a temporary shell in the suspended image with exec;
  • understand much faster whether the problem is a typo, a COPY, the build context, a WORKDIR, a variable, or a missing dependency.

This does not turn a Dockerfile into a regular program, but it gets much closer to the kind of debugging experience people have wanted for years.

Prerequisites

Before opening VS Code, it is worth checking the basics.

1. Updated Docker

The Docker DX documentation recommends Docker Desktop 4.46 or later. If you are not using Docker Desktop, the key point is having Buildx with DAP support.

Check it with:

docker buildx version

The Docker DX debugging guide refers to Buildx v0.28.0 or later.

Then check that the dap subcommand is available.

In Bash:

BUILDX_EXPERIMENTAL=1 docker buildx dap

In PowerShell:

$env:BUILDX_EXPERIMENTAL=1; docker buildx dap

If that command is not available, VS Code will not be able to start build debugging.

2. Install Docker DX

On the Marketplace, the extension is published by Docker under the identifier docker.docker.

The 3 extensions that usually get mixed up

At this point, a lot of people install container-related extensions in VS Code without being fully sure where one ends and the next begins. The current picture looks like this:

ExtensionPublisherBest forImportant notes
ms-azuretools.vscode-dockerMicrosoftCompatibility with the older experienceIt is no longer the classic extension. Today it works as the Docker Extension Pack and includes Container Tools.
ms-azuretools.vscode-containersMicrosoftManaging containers, Compose, and scaffoldingIt replaces the earlier Docker extension functionality from Microsoft.
docker.dockerDockerAdvanced authoring for Dockerfile, Compose, Bake, and build debuggingIt is Docker's official extension for richer editing and Buildx-based build debugging.

What each one does best

Docker DX

  • Dockerfile linting with BuildKit and Buildx;
  • build checks and best practice suggestions;
  • Compose editing with project context;
  • Bake editing;
  • experimental vulnerability analysis;
  • Dockerfile build debugging.

Container Tools

  • explorer for containers, images, networks, and volumes;
  • management commands in the Command Palette;
  • generating Dockerfile, .dockerignore, and Compose files;
  • Compose Up and Compose Up - Select Services;

Microsoft Docker Extension Pack

Today it mostly serves as a transition point. The Marketplace itself explains that the Docker extension became an extension pack and that Container Tools replaced the earlier extension.

Can I use Docker DX and Container Tools together?

Yes. In fact, both Docker and Microsoft describe that setup as valid.

In practice, the split usually looks like this:

The only thing to watch for is duplicated features, especially in Compose. The Docker DX FAQ documents cases where repeated suggestions and hovers may appear.

If that happens, in your VS Code settings, you can disable the overlapping features in one of the extensions.

How to configure Docker DX in VS Code

There are two main ways to start.

Quick start

If you open a Dockerfile and go to Run and Debug, Docker DX can often fill in a minimal configuration automatically.

For a quick test, that is usually enough.

Explicit launch.json configuration

If you want something reproducible, it is better to save the configuration in .vscode/launch.json.

Minimal example:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "dockerfile",
      "request": "launch",
      "name": "Docker: Build",
      "dockerfile": "Dockerfile",
      "contextPath": "${workspaceFolder}"
    }
  ]
}

If you need to, you can also add:

  • target to pick a specific stage;
  • args to pass --build-arg and other build arguments;
  • stopOnEntry set to true to suspend right at the start.

A slightly more complete example:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "dockerfile", // required, must not be modified
      "request": "launch", // required, must not be modified
      "name": "Docker: Build", // required, configurable
      "dockerfile": "Dockerfile", // required, configurable
      "contextPath": "${workspaceFolder}", // optional, defaults to ${workspaceFolder}
      "cwd": "${workspaceFolder}", // optional, defaults to ${workspaceFolder}
      "target": "builder", // optional, should be a build stage in the Dockerfile
      "args": ["--build-arg", "NODE_ENV=development"], // additional arguments for the build command
      "stopOnEntry": false // if the debugger should suspend on the first line, defaults to false
    }
  ]
}

How to debug a Docker build

Once the configuration is in place, the flow is fairly straightforward.

1. Set a breakpoint on a useful instruction

In general, RUN instructions are the best places to stop, because by that point you usually already have useful state to inspect and a concrete command whose effects are easy to validate.

Setting Breakpoints

2. Start the session

You can start the session with F5 or by clicking the start button in the Run and Debug panel.

The build will stop at the marked instruction, or right at the beginning if you use stopOnEntry.

3. Inspect variables

In the Variables panel, VS Code shows ENV values and filesystem state. That is especially useful when you have variable substitution, computed paths, parameterized versions, or stages that behave differently depending on the environment.

Inspecting Variables

4. Open the filesystem explorer

This is one of the most practical parts of the experience. Instead of running docker run and browsing a shell just to confirm whether a file exists, you can inspect the directory tree directly in the debugger.

That is especially useful for catching issues such as:

  • COPY going to the wrong path;
  • an incomplete build context;
  • files excluded by .dockerignore;
  • accidentally creating a file where you expected a directory.

5. Use exec in the Debug Console

When the build is suspended, open the Debug Console, type exec, and press Enter.

If everything works as expected, VS Code opens a new terminal attached to the current image state. From there you can test commands, validate paths, try installing a package, and confirm the fix before writing it back into the Dockerfile.

That shortens the feedback loop a lot between:

  • finding the problem;
  • validating the hypothesis;
  • and writing the correct line back into the Dockerfile.

Opening a Shell Inside the Image Being Built

A practical example

Imagine this Dockerfile:

FROM mcr.microsoft.com/dotnet/sdk:10.0.201 AS build

WORKDIR /src

COPY ./src/*.csproj ./
RUN dotnet restore ./*.csproj

COPY ./ ./

RUN ls -la ./
RUN dotnet build -c Release ./*.csproj
RUN dotnet publish ./*.csproj \
  -c Release \
  --no-build \
  --no-restore \
  -o /publish

FROM mcr.microsoft.com/dotnet/aspnet:10.0.5 AS runtime

COPY --from=build /publish .

ENTRYPOINT ["dotnet", "Demo.Api.dll"]

The problem is COPY ./ ./, which should be COPY ./src ./.

In other words, you expected the contents of the src folder to be available directly inside /src, but COPY ./ ./ copied the repository root.

Without Docker DX, the usual path would be:

  • run docker build;
  • read the error;
  • maybe comment out lines below it;
  • create an intermediate image;
  • open a shell;
  • inspect the filesystem manually;
  • and go back to the file.

With Docker DX:

  1. set a breakpoint on the RUN;
  2. start the debug session;
  3. run exec to open a shell and inspect the filesystem, or use the filesystem explorer directly;
  4. confirm the fix:
  5. go back to the Dockerfile and correct the line;

That kind of simple mistake does not justify five rebuilds in a row. This is exactly where the new experience pays off.

Reset after using the CLI during debugging

This detail is worth being very clear about. When you use exec to open a shell in the suspended image and make manual changes, those changes do not persist when you move to the next step.

The terminal itself warns you about that.

Changes to the container will be reset after the next step is executed.

That is actually a good product decision, because it stops the debug session from depending on ad hoc changes that were never written into the Dockerfile. The shell is there to validate ideas and experiment, not to patch the build outside the file.

In short:

  • use exec to test;
  • copy the real fix into the Dockerfile;
  • restart the debug session if you need to validate again.

Reset after using the CLI during debugging

Other IDEs and editors

Buildx relies on the Debug Adapter Protocol, so the idea is not locked to VS Code.

VS Code

This is still the most mature and direct implementation right now.

Neovim

The Buildx repository points to official support through the plugin.

JetBrains

In Docker's public material, integration with JetBrains IDEs shows up through DAP-related plugins such as LSP4IJ. In practice, that means the integration is plugin-based rather than a native part of Docker DX itself.

If you want the lowest-friction option today, VS Code is still the most direct choice.

Limitations worth knowing about

You can still tell this is a relatively recent feature. It is already useful in day-to-day work, but it still has a few rough edges, many of them coming from Buildx DAP itself.

For example, exec does not always open a shell, the file explorer does not work in every scenario, and stepping can still feel odd in some stages. DAP also has known limits, such as not distinguishing identical FROM directives and not allowing arbitrary pauses.

That said, the direction is good. The feature is already useful today, and the current Buildx DAP documentation still lists limitations and future improvements, so it is reasonable to expect it to keep maturing.

References

Mermaid processor

© 2026 Nelson Nobre. All rights reserved.

Nelson Nobre
We are using cookies to ensure that we give you the best experience on our website. By clicking "Accept", you consent to the use of ALL the cookies. However you may visit Cookie policy to provide a controlled consent.