Self-hosted service
dotnet/dotnet-docker avatar
dotnet/dotnet-docker

dotnet/dotnet-docker: official .NET container images and what each tag actually contains

Official container images for .NET

4,876 stars1,978 forksDockerfileMIT

At a glance

What is it?
The Microsoft-maintained repository behind mcr.microsoft.com/dotnet images publishes seven image families across SDK, runtime, runtime-deps, monitor and Aspire Dashboard. The hard part is not pulling an image, it is picking the variant that matches how you build and ship.
Who is it for?
Adopt these images if you build or run .NET services in containers and want Microsoft-maintained base layers instead of hand-rolled Dockerfiles. Do not adopt the distroless variants if your deployment depends on a shell, a package manager or runtime apt/apk installs, because the README states those images ship none of them.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Dockerfile, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem dotnet/dotnet-docker solves, and who it is for

Anyone packaging a .NET application into a container faces the same decision before writing a single instruction: which base image, built by whom, patched by whom. dotnet/dotnet-docker is Microsoft's answer. It is the repository that produces the official images published under the mcr.microsoft.com/dotnet namespace, and it is maintained by the .NET team rather than by a downstream packager.

The audience is narrower than "everyone using .NET". It is developers and platform engineers who need a container base they do not have to rebuild when a security patch lands, and who want the same image to work on a laptop, in CI and in a cluster. The repository ships seven featured image families, each with its own README: dotnet/sdk, dotnet/aspnet, dotnet/runtime, dotnet/runtime-deps, dotnet/monitor, dotnet/aspire-dashboard and dotnet/samples. That split is the point. A build stage and a runtime stage have different needs, and the project refuses to pretend otherwise.

How the images are organised: SDK, runtime, runtime-deps and distroless

The README describes the variants as offering "different combinations of flexibility and deployment size", and points to a separate image-variants document for the summary. The practical shape is a ladder. dotnet/sdk carries the compiler and tooling and is meant for building. dotnet/aspnet carries the ASP.NET Core runtime for web applications. dotnet/runtime carries the base runtime without the web stack. dotnet/runtime-deps carries only the native dependencies .NET needs, for self-contained applications that bring their own runtime.

On top of that ladder sit the distroless images, and this is where the repository makes its sharpest trade-off. The README states that distroless containers contain only the minimal set of packages .NET needs, with everything else removed, and lists four consequences: a minimized attack surface, smaller deployment sizes, faster start-up, and the following features: a minimal package set, a non-root user by default, no package manager and no shell. .NET offers distroless images for Azure Linux and for Ubuntu (Chiseled).

That last pair of absences is not a footnote. A container with no shell cannot run a shell-form health check, and a container with no package manager cannot install a debugging tool at runtime. The images are also split by architecture and by base OS, which is why the repository carries parallel directory trees under src/ rather than a single Dockerfile.

Running your first .NET container from the official samples

The README gives two runnable examples that need no local .NET installation, because the images are pre-built. The first runs a console application and exits.

bash
docker run --rm mcr.microsoft.com/dotnet/samples

The second runs an ASP.NET Core sample as a web server. It maps host port 8000 to container port 8080 and names the container aspnetcore_sample.

bash
docker run -it --rm -p 8000:8080 --name aspnetcore_sample mcr.microsoft.com/dotnet/samples:aspnetapp

After the application starts, the README says to open http://localhost:8000 in a browser. The port mapping is not optional. The README states that ASP.NET Core apps in official images listen to port 8080 by default starting with .NET 8, and that the container will not be accessible without the -p argument, which takes a host:container mapping. The README also notes you can reach the running site from another machine using a local IP address such as http://192.168.1.18:8000.

For building your own application rather than running a sample, the repository ships a samples/ directory with scenario-specific walkthroughs: build-in-sdk-container.md, run-in-sdk-container.md, run-tests-in-sdk-container.md, build-for-a-platform.md, push-image-to-acr.md, push-image-to-dockerhub.md, deploy-container-to-aci.md, enable-globalization.md and selecting-tags.md. The last one is worth reading before you pin anything, because tag selection is where most teams get the longevity question wrong.

Where these images are the wrong choice

The distroless variants are the clearest case. If your operational model assumes you can exec into a running container to inspect state, or if your entrypoint script installs a certificate package or a native library at start-up, distroless will not accommodate you. The README is explicit that those images have no package manager and no shell. Teams that treat containers as small virtual machines should stay on the non-distroless variants.

The second limitation is architectural rather than documented. This repository builds and publishes images. It does not deploy them, does not orchestrate them, and does not manage secrets. The samples reach out to Azure Container Instances, Azure Container Registry and Docker Hub, but those are examples of consuming the images, not features of the project. If you are looking for a deployment tool, you are in the wrong repository.

A third constraint is inheritance. The image families are layered, so a problem in runtime-deps propagates to runtime, aspnet and sdk. That is efficient for patching and unforgiving when a base OS change lands, which is why the repository tracks base OS versions in directory names under src/ rather than hiding them behind a floating tag.

What you give up compared with building your own base image

The alternative is a hand-written Dockerfile on a generic base such as a plain Debian or Alpine image, with the .NET runtime installed yourself. The difference is not quality, it is who owns the update. With a self-built base you control exactly which packages are present, you can add a shell, a debugger or an APM agent, and you decide when to rebuild. You also inherit the maintenance: tracking .NET patch releases, base OS CVEs and architecture support on your own schedule.

With dotnet/dotnet-docker you get Microsoft's build pipeline and its patch cadence, at the cost of accepting the variant matrix as designed. If your application genuinely needs packages the distroless images omit, the honest answer is to use the non-distroless variant rather than to fight the distroless one. The repository does not offer a middle tier with a shell but no package manager, and the image-variants documentation is where that boundary is described.

The other alternative is building from the SDK image and shipping the SDK image itself, which removes the multi-stage build. It also ships the compiler into production. Nothing in the README recommends this, and the existence of separate runtime and runtime-deps families is the project's position on the matter.

Maintenance, tags and licence

The repository's last push was on 2026-09-22, and it is not archived, so it is being worked on. That matters more here than for a typical library, because a container base image is a security dependency. A stale base image is a stale set of OS packages.

The upgrade cost is concentrated in tag pinning. The repository carries manifest.json, manifest.versions.json and manifest.samples.json at the top level, alongside a src/ tree organised by image family, version and base OS. The samples/selecting-tags.md document exists precisely because the choice between a floating tag and a fully qualified one changes how much work an upgrade is. A fully qualified tag means you find out about a new patch release when you choose to; a floating tag means you find out when your next build pulls something different. The repository also publishes a nightly set of preview images under dotnet/nightly/ for sdk, aspnet, runtime, runtime-deps, monitor and aspire-dashboard, which is where preview .NET versions surface before the stable tags move.

On licensing: the repository is MIT, and the README states that .NET itself is open source under MIT and Apache 2 licences and was contributed to the .NET Foundation by Microsoft in 2014, and that it can be freely adopted by individuals and companies, including for personal, academic or commercial purposes. That covers the source. It is not a statement about the base operating system packages inside each image, and the README does not address those. If your organisation has rules about the licences of everything in a container image, scan the image rather than relying on the repository licence.

Editorial conclusion

Adopt these images if you build or run .NET services in containers and want Microsoft-maintained base layers instead of hand-rolled Dockerfiles. Do not adopt the distroless variants if your deployment depends on a shell, a package manager or runtime apt/apk installs, because the README states those images ship none of them. Before you commit, verify which tag your build actually resolves to against manifest.json and manifest.versions.json in the repository, and confirm your app listens on port 8080 on .NET 8 and later, since the -p mapping in every sample assumes host 8000 to container 8080.

Frequently asked questions

What is dotnet/dotnet-docker and what does it contain?

It is the repository that produces the official .NET container images published under mcr.microsoft.com/dotnet. It ships seven featured image families: sdk, aspnet, runtime, runtime-deps, monitor, aspire-dashboard and samples, each with its own README in the repository.

How do I run a .NET Docker image without installing .NET locally?

Use one of the pre-built sample images. The README's console example is docker run --rm mcr.microsoft.com/dotnet/samples, and the web example is docker run -it --rm -p 8000:8080 --name aspnetcore_sample mcr.microsoft.com/dotnet/samples:aspnetapp, after which you open http://localhost:8000.

Which port does an ASP.NET Core app listen on inside the official image?

Port 8080 by default, starting with .NET 8, according to the README. The README notes that the -p argument maps host port to container port, so -p 8000:8080 exposes the app on host port 8000, and without that mapping the container is not accessible.

What is a distroless .NET image and when should I avoid it?

The README describes distroless images as containing only the minimal set of packages .NET needs, with a non-root user by default, no package manager and no shell, offered for Azure Linux and Ubuntu (Chiseled). Avoid them if your deployment depends on a shell or on installing packages at runtime.

vscode dotnet docker debug

The repository ships a .devcontainer/ directory and a .vscode/ directory at the top level, along with samples/run-in-sdk-container.md and samples/run-tests-in-sdk-container.md, which cover running and testing inside an SDK container. The README does not document a specific Visual Studio Code debugging workflow.

Official sources

  1. dotnet/dotnet-docker on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dotnet-dotnet-docker.svg)](https://hysenlabs.com/projects/dotnet-dotnet-docker)