Skip to main content

Run Blazor in a Docker container with Visual Studio Code Remote Development

The Blazor logo, with a picture of Bit the Raccoon on the right.

Remote Development is by far one of my fave extensions in Visual Studio Code. It’s only available in the Insiders preview for now, but it’s a must-try. What does it do? It basically allows your Visual Studio Code instance to connect to a remote development environment, whether it’s a Virtual Machine, a Windows Subsystem for Linux instance or a Docker container, and use it to compile, execute and debug your code.

Some of the use cases are:

  • I don’t have a Node SDK on my machine, but I’d like to run a ReactJS application
  • I’d prefer not to install a preview-XYZ version of .NET Core 3, it’s just annoying to update it every time there’s a new release, better to just drop a container and recreate it with the latest bits
  • I run a Windows machine, but the tooling and the dependencies of my application run on Linux (e.g. Redis), it would be great to have everything available at the press of an F5

In this article, I’m going to show how we can use it to run and debug a Blazor application in a Docker container, without having to install the SDK.

What do we need as a pre-req?

That’s all 🙂

 

Let’s get started!

After having created an empty folder and opened it in Visual Studio Code, the next step is creating a Container Configuration File. We can easily do it from the command palette:

Creating a Container Configuration File from the command palette:

The add-in comes with quite a substantial list of pre-defined templates, including Node, .NET Core, Java, Azure Functions, etc. You can definitely build your own — they’re quite self-explanatory as we’re going to shortly see — but .NET Core (latest) is the one we want to pick for our example.

Picking the .NET Core (latest) pre-defined template.

This will add two files to our folder:

  • a Dockerfile, which defines the SDK image we’re going to remote on
  • a devcontainer.json file, which indicates to Visual Studio Code how to spin up our dev environment.

 

Preparing our dev environment in Docker

We are going to make a few changes to both of them. Let’s head to the Dockerfile on GitHub first:

Dockerfile (from Github)

Two changes here:

  • We are “inheriting” our image from the .NET Core SDK 3.0, by selecting the sdk:3.0 base image
  • By default, the base SDK comes only with the server side version of Blazor pre-installed. Therefore, in line #16, we install the preview project templates for Blazor Client Side.

The other file is devcontainer.json and contains some additional specs for Visual Studio Code on how to spin up our environment:

{
    "name": "C# (.NET Core Latest)",
    "dockerFile": "Dockerfile",

    "appPort": [
      "5000:5000",
      "5001:5001"
    ],

    "shutdownAction": "stopContainer",

    "extensions": [
      "ms-vscode.csharp"
    ]
}

The content is pretty simple:

  • in line #3 we reference the Dockerfile we want to use for the image
  • in line #5 we expose a couple of ports to the host machine
  • in line #10 we select that we want to stop the container when we quit Visual Studio Code
  • in line #12 we can pre-install some IDE extensions if needed. I’ve left this to the default value, which is C# for Visual Studio Code.

That’s really it! At this point all we have to do is selecting the “Reopen folder in container” option to start playing with our new SDK:

Selecting the “Reopen folder in container” option

 

Enter Remote Development Mode

As soon as we do it, Visual Studio Code will automatically start building the Docker image (it might take a few minutes the first time as we have to download a couple of GBs), then it will spin up a container and connect to it, as we can see from the status bar on the bottom left corner.

The status bar on the bottom left corner

Also, executing a docker ps command shows the running container we are currently using:

A 'docker ps' command showing the running container we are currently using.

This is now our playground: it mounts the folder in our host machine as a container volume, which means that we can seamlessly edit it from the host machine or within the container itself.

For example, we can create a new terminal instance — which will open a bash shell in the container — and do a dotnet new blazorhosted to create a new solution using the Blazor ASP.NET Core Hosted template.

Using 'dotnet new blazorhosted' to create a new solution using the Blazor ASP.NET Core Hosted template, in a bash shell

This will generate the 3 projects this template is made of.

The three projects that were generated.

 

Let’s run it!

How do we run it? Well, it’s just a matter of pressing F5 and select the .NET Core template to create the proper launch.json file:

Pressing F5 to select the .NET Core template and creating the proper launch.json file

Before launching it, we might want to force Kestrel to use the two ports 5000 and 5001 we have previously exposed. We can do it by adding an ASPNETCORE_URLS environment variable:

"env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://*:5000;https://*:5001"
},

We are finally ready! Press F5 again and Visual Studio Code will start our application and attach to the process. If we then open a browser and head to http://localhost:5000 we’ll see our new shiny Blazor application up and running as expected.

The Blazor application appearing at localhost:5000

What about the debugger? Well, we have to think about *what* we want to debug: it works as expected for the code running on the server-side. We can in fact set a breakpoint and see it being hit when we come across it.

Setting a breakpoint

Unfortunately client-side Blazor is not supported, and this is for a couple of reasons:

  1. The integration with Visual Studio Code is not there yet
  2. A Web Assembly application runs in the browser, so you need the debugger to attach your msedge.exe or your chrome.exe processes, which is obviously not possible when running in remote development mode.

No problems whatsoever with Blazor running server-side: in this mode, the page code runs on the server, and the client communicates with it via websocket. Therefore, we can set a breakpoint in a .razor page file and the debugger will stop the execution as expected.

Setting a breakpoint in a .razor page file

 

Wrapping up

The new Remote Development extension is a deal breaker in terms of flexibility of Visual Studio Code: it guides us through the process of attaching to a development environment sitting either in a Virtual Machine, in WSL or on a Docker container.

The latter is probably the most interesting option in a number of different contexts, such as spiking different frameworks/SDKs or running Linux components in a Windows system.

During this article we’ve investigated how to use it to run a Blazor ASP.NET Core application, consuming it through our browser and debugging it in Visual Studio Code.