Agent Skills: .NET Dockerfile Generator

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.

UncategorizedID: jkappers/agent-skills/dotnet-dockerfile

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jkappers/agent-skills/tree/HEAD/skills/dotnet-dockerfile

Skill Files

Browse the full folder contents for dotnet-dockerfile.

Download Skill

Loading file tree…

skills/dotnet-dockerfile/SKILL.md

Skill Metadata

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

# 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 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:

<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 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