Inside a Dev Container: How to Standardize .NET Development — hero banner

July 29, 2025·4 min read

Spinning up a .NET application locally in a corporate environment often turns into an exercise in frustration. Developers deal with inconsistent SDK versions, corporate proxy certificates, authentication errors against private feeds, and missing tools. Instead of writing code, they spend hours configuring machines.

Dev Containers fix that.

Dev Containers are Docker-based development environments defined using .devcontainer config files and launched via Visual Studio Code. By defining your development environment as code, Dev Containers let you version, automate, and standardize everything needed to get up and running — regardless of which machine or developer is involved.

Why This Matters

Problem                          →  Solved by
---------------------------------------------------------------
Proxy certificate errors          Trusting Zscaler root certs
HTTPS dev environment             dotnet dev-certs https
NuGet feed authentication         dotnet nuget add source with a secure PAT
"It works on my machine" bugs     Identical container builds
Inconsistent SDKs and tools       Pinned versions in Docker image

Whether onboarding new developers or debugging issues across machines, Dev Containers give us a consistent, secure, and versioned workspace — every time.

The Dockerfile: Building a Reliable .NET Environment

We use Microsoft’s official .NET SDK 8.0 image as our base:

FROM mcr.microsoft.com/dotnet/sdk:8.0

Key Features

Private NuGet feed authentication
We use a Personal Access Token (PAT) to connect to Azure DevOps feeds securely. This is injected as a build argument and configured at build time:

RUN dotnet nuget add source "${ADO_FEED_URL}"     --name "AzureDevOps"     --username "${ADO_FEED_USER}"     --password "${ADO_FEED_PAT}"     --store-password-in-clear-text

Is --store-password-in-clear-text secure?
This option is required when using dotnet nuget add source with credentials. While storing the password in plain text is not ideal for production containers, it is acceptable in temporary dev containers that are not distributed or reused. To enhance security, avoid checking in any secrets and limit access to the container environment.

Zscaler root certificate installation
In a corporate environment with deep packet inspection (like Zscaler), HTTPS traffic can be intercepted unless root certificates are trusted. We add the cert manually:

COPY ZscalerRootCerts/ZscalerRootCertificate.crt /usr/local/share/ca-certificates/
RUN update-ca-certificates

Dev HTTPS certificate setup
We generate and trust the HTTPS developer certificate so ASP.NET Core can serve over HTTPS locally:

RUN dotnet dev-certs https --clean &&     dotnet dev-certs https --trust || true

Standard tooling
We install common tools (curl, git, vim, etc.) so the container can support debugging, scripting, and customization.

Complete Dockerfile

FROM mcr.microsoft.com/dotnet/sdk:8.0

# Accept build args
ARG ADO_FEED_URL
ARG ADO_FEED_USER
ARG ADO_FEED_PAT

# Required ENV vars
ENV DOTNET_USE_POLLING_FILE_WATCHER=1     ASPNETCORE_URLS=http://+:8080     DOTNET_RUNNING_IN_CONTAINER=true     NUGET_CREDENTIALPROVIDER_SESSIONTOKENCACHE_ENABLED=true

# Install tools
RUN apt-get update &&     apt-get install -y --no-install-recommends     ca-certificates     curl     wget     gnupg     git     unzip     bash     vim &&     apt-get clean &&     rm -rf /var/lib/apt/lists/*

# Add Zscaler certs
COPY ["ZscalerRootCerts/ZscalerRootCertificate.crt", "/usr/local/share/ca-certificates/ZscalerRootCertificate.crt"]
COPY ["ZscalerRootCerts/ZscalerRootCertificate.crt", "/etc/ssl/certs/ZscalerRootCertificate.crt"]
RUN update-ca-certificates

# Set up NuGet source securely
RUN dotnet nuget add source "${ADO_FEED_URL}"     --name "AzureDevOps"     --username "${ADO_FEED_USER}"     --password "${ADO_FEED_PAT}"     --store-password-in-clear-text

# Install ASP.NET dev certs
RUN dotnet dev-certs https --clean &&     dotnet dev-certs https --trust || true

# Final devcontainer setup
WORKDIR /workspace
EXPOSE 80
EXPOSE 443

devcontainer.json: Wiring VS Code into the Container

The devcontainer.json file tells VS Code how to build and configure the container:

"dockerFile": "../Dockerfile",
"build": {
  "args": {
    "ADO_FEED_URL": "...",
    "ADO_FEED_USER": "...",
    "ADO_FEED_PAT": "..."
  }
},
"workspaceFolder": "/workspace",
"remoteUser": "root",
"customizations": {
  "vscode": {
    "extensions": ["ms-dotnettools.csharp"]
  }
}

This defines the build args, mounts your local code folder into /workspace, sets up common VS Code extensions, and runs the container as root for ease of configuration.

End Result

Every developer on the team — regardless of their host OS or permissions — can run:

dotnet restore
dotnet run

…and everything works.

There are no surprises around certificate trust, no more configuring private feeds manually, and no wasted time figuring out what’s missing from a machine. It just works, every time.

📁 Sample Folder Structure

MyDotNetApp/
├── .devcontainer/
│   ├── devcontainer.json
│   └── Dockerfile
│
├── ZscalerRootCerts/
│   └── ZscalerRootCertificate.crt
│
├── src/
│   └── MyDotNetApp/
│       ├── MyDotNetApp.csproj
│       ├── Program.cs
│       └── Startup.cs
│
├── MyDotNetApp.sln
└── README.md

How to Use

If you’re new to Dev Containers, getting started is simple — especially in Visual Studio Code.

Prerequisites

  • Install Docker Desktop (or Podman + Docker CLI on Linux)
  • Install Visual Studio Code
  • Install the Dev Containers extension

Steps to Use

  1. Clone your repository and make sure the .devcontainer folder exists.
  2. Open the repo in VS Code.
  3. Press F1 (or Ctrl+Shift+P) and run:
    Dev Containers: Reopen in Container
  4. VS Code will build and launch the container, automatically installing dependencies, mounting your source code, and setting up extensions.
  5. Once the container opens, use the terminal or debugging features as you normally would. You’re now working inside the container.

Add a launch.json to launch the app

For .NET apps, add a .vscode/launch.json file to let VS Code run and debug the app inside the container:

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": ".NET Core Launch (web)",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build",
      "program": "${workspaceFolder}/bin/Debug/net8.0/MyDotNetApp.dll",
      "args": [],
      "cwd": "${workspaceFolder}",
      "stopAtEntry": false,
      "serverReadyAction": {
        "action": "openExternally",
        "pattern": "Now listening on: (https?://\\S+)"
      },
      "env": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "sourceFileMap": {
        "/Views": "${workspaceFolder}/Views"
      }
    }
  ]
}

Final Thoughts

Dev Containers have turned complex setup into a repeatable, version-controlled process. In enterprise environments, where security, repeatability, and private package feeds add complexity, Dev Containers let developers focus on building software instead of fixing environments.

Learn more: https://containers.dev

Enjoyed this post? Give it a clap!

Comments