.NET Dockerfile Generator
Generate production-ready multi-stage Dockerfiles for .NET applications.
Workflow
- •Determine .NET version (8, 9, or 10) from project file or ask user
- •Identify project type: ASP.NET Core web app, API, worker service, or console app
- •Locate project file path (e.g.,
src/MyApp/MyApp.csproj) - •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, and10.0-noble-chiseledall 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
# 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:
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:
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:
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 tagged10.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:
<PropertyGroup> <PublishAot>true</PublishAot> </PropertyGroup>
Limitations: No runtime reflection, not all libraries compatible.
Dockerfile-less Container Publishing
.NET 10 supports publishing containers directly from the SDK—no Dockerfile required:
# 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:
<PropertyGroup> <!-- Control image format: Docker or OCI --> <ContainerImageFormat>OCI</ContainerImageFormat> <!-- Customize base image --> <ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled</ContainerBaseImage> <!-- Set image name and tag --> <ContainerRepository>myapp</ContainerRepository> <ContainerImageTag>1.0.0</ContainerImageTag> </PropertyGroup>
Development Stage
Add a development stage for local dev with hot reload:
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:
RUN --mount=type=cache,target=/root/.nuget/packages \
dotnet restore
SHA Pinning for Production
Pin exact image digests for reproducibility and security:
FROM mcr.microsoft.com/dotnet/sdk:10.0@sha256:abc123... AS build
To find the current digest, run:
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:
**/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 appfor 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