---
name: dotnet-dockerfile
description: Create optimized, secure multi-stage Dockerfiles for .NET Core and ASP.NET Core applications. Use when (1) creating a new Dockerfile for a .NET project, (2) containerizing a .NET application, (3) optimizing an existing .NET Dockerfile, (4) setting up Docker for .NET 6/7/8+ apps, or (5) user mentions .NET and Docker/container together.
---
# .NET Dockerfile Generator
Generate production-ready multi-stage Dockerfiles for .NET applications.
## Workflow
1. Determine .NET version (8, 9, or 10) from project file or ask user
2. Identify project type: ASP.NET Core web app, API, worker service, or console app
3. Locate project file path (e.g., `src/MyApp/MyApp.csproj`)
4. Choose optimization: standard, chiseled (smaller), or native AOT (smallest/fastest startup)
> **Breaking Change in .NET 10**: Ubuntu is now the default Linux distribution for all .NET container images. Debian images are no longer provided. Tags like `10.0`, `10.0-noble`, and `10.0-noble-chiseled` all use Ubuntu 24.04 "Noble Numbat".
## Image Selection Guide
| Scenario | Runtime Image | Compressed Size |
|----------|--------------|-----------------|
| Standard ASP.NET | `aspnet:10.0` | ~92 MB |
| Smaller footprint | `aspnet:10.0-alpine` | ~52 MB |
| Production (recommended) | `aspnet:10.0-noble-chiseled` | ~53 MB |
| Azure Linux distroless | `aspnet:10.0-azurelinux3.0-distroless` | ~55 MB |
| Self-contained | `runtime-deps:10.0-noble-chiseled` | ~22 MB |
| Native AOT | `runtime-deps:10.0-noble-chiseled` | ~12 MB |
**Chiseled images**: Ubuntu-based, no shell, no package manager, non-root by default. Recommended for production.
**Azure Linux distroless**: Alternative to chiseled with Microsoft supply chain security. Similar benefits: no shell, non-root by default.
## Standard Pattern
```dockerfile
# syntax=docker/dockerfile:1
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy project files and restore (cache layer)
COPY *.csproj ./
RUN dotnet restore
# Copy source and publish
COPY . .
RUN dotnet publish -c Release -o /app/publish
# Runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS production
WORKDIR /app
COPY --from=build /app/publish .
USER app
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.dll"]
```
## Multi-Project Solution Pattern
For solutions with multiple projects, copy all .csproj files first:
```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy solution and project files (cache layer)
COPY *.sln ./
COPY src/MyApp/*.csproj ./src/MyApp/
COPY src/MyApp.Core/*.csproj ./src/MyApp.Core/
COPY tests/MyApp.Tests/*.csproj ./tests/MyApp.Tests/
RUN dotnet restore
# Copy everything and publish
COPY . .
RUN dotnet publish src/MyApp -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS production
WORKDIR /app
COPY --from=build /app/publish .
USER app
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.dll"]
```
## Self-Contained Deployment
Bundles .NET runtime with app, enables smallest runtime-deps images:
```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY *.csproj ./
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish \
--self-contained true \
-r linux-x64
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled AS production
WORKDIR /app
COPY --from=build /app/publish .
USER app
EXPOSE 8080
ENTRYPOINT ["./MyApp"]
```
## Native AOT (Fastest Startup)
Compiles to native code. Smallest image (~12 MB), fastest startup:
```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0-aot AS build
WORKDIR /src
COPY *.csproj ./
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled AS production
WORKDIR /app
COPY --from=build /app/publish .
USER app
EXPOSE 8080
ENTRYPOINT ["./MyApp"]
```
**Dedicated AOT SDK images** include native compilation prerequisites:
- `mcr.microsoft.com/dotnet/sdk:10.0-aot` (Ubuntu, also tagged `10.0-noble-aot`)
- `mcr.microsoft.com/dotnet/sdk:10.0-alpine-aot`
- `mcr.microsoft.com/dotnet/sdk:10.0-azurelinux3.0-aot`
**Requires in .csproj:**
```xml
true
```
**Limitations**: No runtime reflection, not all libraries compatible.
## Dockerfile-less Container Publishing
.NET 10 supports publishing containers directly from the SDK—no Dockerfile required:
```bash
# Publish to local container runtime
dotnet publish --os linux --arch x64 /t:PublishContainer
# Publish directly to a registry (no Docker needed)
dotnet publish --os linux --arch x64 /t:PublishContainer \
-p ContainerRegistry=ghcr.io \
-p ContainerRepository=myorg/myapp
# Multi-architecture publishing
dotnet publish /t:PublishContainer \
-p ContainerRuntimeIdentifiers="linux-x64;linux-arm64"
```
**Project file configuration:**
```xml
OCI
mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled
myapp
1.0.0
```
## Development Stage
Add a development stage for local dev with hot reload:
```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS base
WORKDIR /src
COPY *.csproj ./
RUN dotnet restore
FROM base AS development
COPY . .
ENTRYPOINT ["dotnet", "watch", "run"]
FROM base AS build
COPY . .
RUN dotnet publish -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS production
WORKDIR /app
COPY --from=build /app/publish .
USER app
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApp.dll"]
```
Build development: `docker build --target development -t myapp:dev .`
## Cache Optimization
Use BuildKit cache mount for NuGet packages:
```dockerfile
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet restore
```
## SHA Pinning for Production
Pin exact image digests for reproducibility and security:
```dockerfile
FROM mcr.microsoft.com/dotnet/sdk:10.0@sha256:abc123... AS build
```
To find the current digest, run:
```bash
docker pull mcr.microsoft.com/dotnet/sdk:10.0
docker inspect --format='{{index .RepoDigests 0}}' mcr.microsoft.com/dotnet/sdk:10.0
```
## Required .dockerignore
Always create `.dockerignore`:
```dockerignore
**/bin/
**/obj/
**/.vs/
**/.vscode/
**/node_modules/
**/*.user
**/*.userosscache
**/*.suo
*.md
.git
.gitignore
Dockerfile*
docker-compose*
```
## Verification Checklist
- SDK image for build stage, runtime/aspnet for production
- .NET 8+ uses port 8080 (not 80)
- Copy .csproj files before source for cache efficiency
- Use `USER app` for non-root execution (chiseled images have this user)
- Chiseled images have no shell—use exec form only, no `&&` chains in final stage
- Include .dockerignore to exclude build artifacts
- Match runtime identifier (`-r linux-x64`) for self-contained builds
- Consider SHA pinning for production reproducibility