Day 1 - Dev Containers

TJ GokkenTJ Gokken
8 min read

Introduction

You know the drill. It happened to the best of us.

You clone a project, you’re excited to get started... and then it hits you - missing SDKs, version mismatches, half an hour fighting Docker installs, weird Windows file paths, and that nagging voice inside saying, "Wasn’t tech supposed to make life easier?"

The answer to that question is yes: Dev Containers fix those issues for you.

They wrap your entire dev environment — tools, SDKs, extensions, the works — inside a portable Docker container. No more "works on my machine" moments. No more cluttering your laptop with random dependencies.

Just code, clean and simple.

And if you’re on Windows?

Well, Microsoft realized the benefits of this technology and they created a Windows Subsystem for Linux - I am not making this up, that is literally what it is called: WSL. There is even a second version of it. This technology makes running dev containers smooth - especially if you are using a Windows machine.

💡
Come to think about it, it really should be called LSW, because it is a Linux Subsystem for Windows, that is Linux is running inside Windows. Why did they do it this way? Probably marketing and, don’t get me wrong I do like them but, it is Microsoft after all. I think WWL (Windows Wrapper for Linux) is a more suiting name, but maybe they thought it is too close to WWF :-)

In this mini-course, you're going from Zero to Hero in two days:

  • Day 1: Build a real C# app inside a container — and run it. Why C#? I did say, I like Microsoft. Joking aside, the language does not matter. The principles apply just the same.

  • Day 2: Customize, debug, and extend your environment without losing your mind.

Alright, enough talk. Let’s get coding. The code is not going to write itself (unless you are using AI, but even then you need to understand what’s going on).

The goal here is to get this running as fast as possible. So, we are going to do this in 5 easy steps.

Step 1: Prep Your Machine (WSL + Docker)

If you already have WSL 2 and Docker Desktop installed, you can skip this part. If not, here’s the bare minimum to get going:

\=> Install WSL 2

Open PowerShell as Admin and run:

wsl --install

This installs Ubuntu and sets it to WSL 2 by default. Done.

\==> Install Docker Desktop

Grab it from docker.com. During setup, make sure you enable “Use WSL 2 based engine.”

\===> Connect Docker to WSL

In Docker Desktop settings:

  • Go to Settings > Resources > WSL Integration

  • Turn it ON for your Ubuntu distro.

💡
After you run wsl --install, Windows Terminal will automatically add Ubuntu as a new shell option. You can open it anytime, just like you would PowerShell or Command Prompt — but this one is a full Linux environment running inside Windows. (On the terminal window, just click on the chevron).

Quick test to verify that docker is installed:

docker --version

If you see the Docker version from inside WSL, you’re ready.

That’s it. No deep dive into Linux. No lectures about kernels. You now have a clean environment ready for containers.

Step 2: Create a Minimal C# Web API Project

Now that we have our environment ready, let’s make something real.

In your WSL shell or normal terminal - this will create a folder and solution in that folder so do this where you want this to happen:

dotnet new webapi -n DevContainerDemo
cd DevContainerDemo
code .

This spins up a simple ASP.NET Core Web API project and opens it in VS Code.

Done and dusted. It’s easy isn’t it?

Step 3: Add Dev Container Support

Here’s where the magic happens.

In VS Code:

  • Hit Cmd+Shift+P (or Ctrl+Shift+P on Windows)

  • Make sure you see a > character in the top search box. If not, manually add it.

  • Type: Dev Containers: Add Dev Container Configuration Files

  • Pick: C# (.NET)

  • Pick: .NET 8.0 (or whatever you’re targeting)

After asking a bunch of other questions (don’t worry most of them are self-explanatory and you can just breeze through them), you will see a little message on the bottom right:

Just click that button (not the Don’t Show Again one). VSC will inform you that it is working but go ahead and click on that Show Log link and watch magic happen.

When VSC is done, you will see that it created a .devcontainer folder for you with some files in it, such as devcontainer.json and a Dockerfile.

Don’t worry about these files. You’ll barely touch them — VS Code handles most of the heavy lifting.


🚨Alarm: Things Did Not Go As Expected

You may run into an issue and VSC may tell you that it ran into a problem. If your error contains the following text;

Unable to find fallback package folder 'C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages'.

it means the language server (called Omnisharp) inside VS Code throws a fit trying to load your .csproj project because it expects local Windows paths that don't exist inside your container. That path that starts with C:/Program Files… does not exist in Linux.

So, let’s tell Omnisharp to relax and follow our instructions and everything is going to be alright:

  1. In your project workspace (root folder — next to your .sln file), create a file called: omnisharp.json and add this content to it:
{
    "RoslynExtensionsOptions": {
        "enableAnalyzersSupport": true,
        "enableImportCompletion": true,
        "locationPaths": []
    },
    "MSBuild": {
        "LoadProjectsOnDemand": true,
        "UseLegacySdkResolver": false
    }
}

This will tell Omnisharp to run in standalone mode and not try to load a full Visual Studio MSBuild environment.

Let’s also update .devcontainer/devcontainer.json and add (or uncomment - see below)postCreateCommand like this:

"postCreateCommand": "dotnet restore"

Chances are you already have this line but it is commented out. Just uncomment it and do not forget to add a comma after the line before it as it can get confusing with all the comments in that file.

This is optional but just for good measure, let’s also force Omnisharp to run with the Linux SDK. Add this line to .vscode/settings.json file (create the file if it does not exist):

  {
    "omnisharp.useModernNet": true
  }
💡
Don't confuse this with appsettings.json — that's for your C# app itself, not for VS Code settings.

Once you did all that, type this at the top in the text box in VSC:

>Dev Containers: Rebuild and Reopen in Container

Once you hit enter, VSC will rebuild the docker image if needed, restart the container, reload your project inside it and refresh Omnisharp and all the extensions inside the container.

Seems complicated, but in about 3 minutes, you will be done.

💡
After making changes to your devcontainer settings, always use "Dev Containers: Rebuild and Reopen in Container" to reload your project cleanly inside the updated container. No need to fully restart VS Code!

Step 4: Open Inside the Dev Container

If you ran into a problem above (Omnisharp freaking out), you may skip this step as we already took care of this. If not, please read on.

In VS Code:

  • Hit Cmd+Shift+P

  • Type: Dev Containers: Reopen in Container

VS Code will build the Docker image, create a container, install the right SDKs inside it and, reopen your project inside that container.

You’ll barely notice except your terminal prompt might change slightly (as it’s now running inside Linux like the screenshot below).

Step 5: Run Your App Inside the Container

Now let’s run our app. Run this command in your VSC terminal:

dotnet run

You should see VSC informing you that it is listening at some port. Make a note of that port and then visit this address on your browser

http://localhost:5000/weatherforecast

Boom!

Your C# app is running — inside a Docker container — inside WSL — without wrecking your machine.

💡
You might see a warning when you run your app. This is happening because .NET Core wants to support HTTPS by default. However, we did not configure a certificate in the container, so it cannot run in HTTPS. So, it complains but moves on quickly and happily serves the pages on HTTP. Such is life.

Quick Recap

Let’s see what we have just done:

  • We ran Linux under Windows

  • We did not clone any SDKs

  • Nor did we install anything in Linux

  • We didn’t have to manage versions, global settings, weird environment paths

Yet, still our C# app built and ran, in Linux. We are running .NET inside a fresh, isolated Linux container -
not our host Windows, not WSL, not Docker Desktop directly - but inside a brand new Linux world spun up just for us by the Dev Container.

This is the experience we would have had, if this was an app developed by another developer and given to us. Now, think what we would have to do to make it run without a dev container. This is the real power of these dev containers. Our Windows environment is clean. Our Linux container is ready to rock.

Once you do it this way, you may not want to go back.

What's Next (Day 2)

Tomorrow, we’ll make it even better:

  • Customize the container

  • Add auto-restore commands

  • Install useful extensions

  • Set up proper debugging

  • Maybe even spin up a lightweight database

Because if you're going to containerize your dev environment, you might as well do it right.

End of Day 1

Yay, we survived. Let’s go grab a coffee or tea. ☕ 🎉

0
Subscribe to my newsletter

Read articles from TJ Gokken directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

TJ Gokken
TJ Gokken

TJ Gokken is an Enterprise AI/ML Integration Engineer with a passion for bridging the gap between technology and practical application. Specializing in .NET frameworks and machine learning, TJ helps software teams operationalize AI to drive innovation and efficiency. With over two decades of experience in programming and technology integration, he is a trusted advisor and thought leader in the AI community