بنقرة واحدة
dotnet-containers
Containerizing .NET apps. Multi-stage Dockerfiles, SDK container publish (.NET 8+), rootless.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Containerizing .NET apps. Multi-stage Dockerfiles, SDK container publish (.NET 8+), rootless.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Guidelines for writing TUnit tests in .NET, including setup, assertions, async testing, and best practices. Use when writing unit tests with TUnit framework, setting up TUnit in a .NET project, or migrating from other test frameworks to TUnit.
Hosted services, background jobs, outbox patterns, and graceful shutdown handling for ASP.NET Core applications. Includes patterns for reliable job processing, distributed systems, and lifecycle management. Use when implementing background processing in ASP.NET Core applications, handling outbox patterns for reliable message delivery, or managing graceful service shutdown.
Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, versioning, naming conventions, parameter ordering, and return types for NuGet packages and distributed systems. Use when designing public APIs for NuGet packages or libraries, making changes to existing public APIs, planning wire format changes for distributed systems, or reviewing pull requests for breaking changes.
Write modern, high-performance C# code using records, pattern matching, value objects, async/await, Span<T>/Memory<T>, and best-practice API design patterns. Emphasizes functional-style programming with C# 12+ features. Use when writing new C# code or refactoring existing code, designing public APIs for libraries or services, optimizing performance-critical code paths, or building async/await-heavy applications.
Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Covers both high-level abstractions and low-level synchronization primitives. Use when deciding how to handle concurrent operations in .NET, evaluating whether to use async/await, Channels, or Akka.NET, or managing state across multiple concurrent entities.
Design .NET types for performance. Covers struct vs class decision matrix, sealed by default, readonly structs, ref struct and Span/Memory selection, FrozenDictionary, ValueTask, and collection return types. Use when designing new types and APIs, reviewing code for performance issues, choosing between class, struct, and record, or working with collections and enumerables.
استنادا إلى تصنيف SOC المهني
| name | dotnet-containers |
| description | Containerizing .NET apps. Multi-stage Dockerfiles, SDK container publish (.NET 8+), rootless. |
Best practices for containerizing .NET applications. Covers multi-stage Dockerfile patterns, the dotnet publish container image feature (.NET 8+), rootless container configuration, optimized layer caching, and container health checks.
Out of scope: DI container mechanics and service lifetimes -- see [skill:dotnet-csharp-dependency-injection]. Kubernetes deployment manifests and Docker Compose orchestration are covered in [skill:dotnet-container-deployment]. CI/CD pipeline integration for building and pushing images -- see [skill:dotnet-gha-publish] and [skill:dotnet-ado-publish]. Testing containerized applications -- see [skill:dotnet-integration-testing] for Testcontainers patterns.
Cross-references: [skill:dotnet-observability] for health check patterns, [skill:dotnet-container-deployment] for deploying containers to Kubernetes and local dev with Compose, [skill:dotnet-artifacts-output] for Dockerfile path adjustments when using centralized build output layout.
Multi-stage builds separate the build environment from the runtime environment, producing minimal final images.
# Stage 1: Build
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy project files first for layer caching
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
COPY ["src/MyApi.Core/MyApi.Core.csproj", "src/MyApi.Core/"]
COPY ["Directory.Build.props", "."]
COPY ["Directory.Packages.props", "."]
RUN dotnet restore "src/MyApi/MyApi.csproj"
# Copy everything else and build
COPY . .
WORKDIR "/src/src/MyApi"
RUN dotnet publish -c Release -o /app/publish --no-restore
# Stage 2: Runtime
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
EXPOSE 8080
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
Order COPY instructions from least-frequently-changed to most-frequently-changed:
dotnet restore -- cached until project files changedotnet publish -- runs only when source or restore layer changes# Good: restore layer is cached when only source changes
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
RUN dotnet restore
COPY . .
RUN dotnet publish
# Bad: restore runs on every source change
COPY . .
RUN dotnet restore
RUN dotnet publish
For multi-project solutions, copy all .csproj files and the solution file to enable a single restore:
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
# Copy solution and all project files for restore caching
COPY ["MyApp.sln", "."]
COPY ["Directory.Build.props", "."]
COPY ["Directory.Packages.props", "."]
COPY ["src/MyApi/MyApi.csproj", "src/MyApi/"]
COPY ["src/MyApi.Core/MyApi.Core.csproj", "src/MyApi.Core/"]
COPY ["src/MyApi.Infrastructure/MyApi.Infrastructure.csproj", "src/MyApi.Infrastructure/"]
RUN dotnet restore
COPY . .
RUN dotnet publish "src/MyApi/MyApi.csproj" -c Release -o /app/publish --no-restore
Starting with .NET 8, dotnet publish can produce OCI container images directly without a Dockerfile. This uses the Microsoft.NET.Build.Containers SDK (included in the .NET SDK).
# Publish as a container image to local Docker daemon
dotnet publish --os linux --arch x64 /t:PublishContainer
# Publish to a remote registry
dotnet publish --os linux --arch x64 /t:PublishContainer \
-p:ContainerRegistry=ghcr.io \
-p:ContainerRepository=myorg/myapi
Configure container properties in the .csproj:
<PropertyGroup>
<ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:10.0</ContainerBaseImage>
<ContainerImageName>myapi</ContainerImageName>
<ContainerImageTag>$(Version)</ContainerImageTag>
</PropertyGroup>
<ItemGroup>
<ContainerPort Include="8080" Type="tcp" />
</ItemGroup>
<PropertyGroup>
<!-- Use chiseled (distroless) base image for smaller attack surface -->
<ContainerBaseImage>mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled</ContainerBaseImage>
<!-- Run as non-root user (default for chiseled images) -->
<ContainerUser>app</ContainerUser>
</PropertyGroup>
<ItemGroup>
<!-- Environment variables -->
<ContainerEnvironmentVariable Include="ASPNETCORE_URLS" Value="http://+:8080" />
<ContainerEnvironmentVariable Include="DOTNET_RUNNING_IN_CONTAINER" Value="true" />
<!-- Labels -->
<ContainerLabel Include="org.opencontainers.image.source" Value="https://github.com/myorg/myapi" />
</ItemGroup>
| Scenario | Recommendation |
|---|---|
| Simple single-project API | dotnet publish /t:PublishContainer -- less boilerplate |
| Multi-stage build with native dependencies | Dockerfile -- full control over build environment |
Need to install OS packages (e.g., libgdiplus) | Dockerfile -- RUN apt-get install not available in SDK publish |
| CI/CD with complex build steps | Dockerfile -- explicit, reproducible |
| Quick local container testing | dotnet publish /t:PublishContainer -- fastest iteration |
| Image | Use Case | Size |
|---|---|---|
mcr.microsoft.com/dotnet/aspnet:10.0 | ASP.NET Core apps (Ubuntu) | ~220 MB |
mcr.microsoft.com/dotnet/aspnet:10.0-alpine | ASP.NET Core apps (Alpine, smaller) | ~110 MB |
mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled | Distroless (no shell, no package manager) | ~110 MB |
mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled-extra | Chiseled + globalization + time zones | ~130 MB |
mcr.microsoft.com/dotnet/runtime:10.0 | Console apps, worker services | ~190 MB |
mcr.microsoft.com/dotnet/runtime-deps:10.0 | Self-contained/AOT apps (runtime not needed) | ~30 MB |
aspnet for web apps, runtime for worker serviceschiseled variants (no shell, no root user, no package manager)chiseled-extra if your app uses culture-specific formatting or time zonesruntime-deps -- the runtime is bundled in your appRunning containers as non-root reduces the attack surface. .NET 8+ chiseled images run as non-root by default.
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
# Create non-root user and switch to it
RUN adduser --disabled-password --gecos "" --uid 1001 appuser
USER appuser
COPY --from=build --chown=appuser:appuser /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
Chiseled images include a pre-configured app user (UID 1654). No additional configuration needed:
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS runtime
WORKDIR /app
# Already runs as non-root 'app' user (UID 1654)
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
Non-root users cannot bind to ports below 1024. ASP.NET Core defaults to port 8080 in containers (set via ASPNETCORE_HTTP_PORTS):
# Default in .NET 8+ container images -- no explicit config needed
# ASPNETCORE_HTTP_PORTS=8080
# If you need a different port:
ENV ASPNETCORE_HTTP_PORTS=5000
EXPOSE 5000
Health checks allow container runtimes to monitor application readiness. The application-level health check endpoints (see [skill:dotnet-observability]) are consumed by Docker and Kubernetes probes.
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
WORKDIR /app
# Health check using curl (not available in chiseled images)
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/health/live || exit 1
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
For chiseled images (no curl), use a dedicated health check binary or rely on orchestrator-level probes (Kubernetes httpGet, Docker Compose test):
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled AS runtime
WORKDIR /app
# No HEALTHCHECK directive -- use orchestrator probes instead
# See [skill:dotnet-container-deployment] for Kubernetes probe configuration
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyApi.dll"]
Register health check endpoints in your application (see [skill:dotnet-observability] for full guidance):
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), tags: ["live"])
.AddNpgSql(
builder.Configuration.GetConnectionString("DefaultConnection")!,
name: "database",
tags: ["ready"]);
var app = builder.Build();
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("live")
});
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
Always include a .dockerignore to exclude unnecessary files from the build context:
**/.git
**/.vs
**/.vscode
**/bin
**/obj
**/node_modules
**/*.user
**/*.suo
**/Dockerfile*
**/docker-compose*
**/.dockerignore
**/README.md
**/LICENSE
If your app needs globalization support (culture-specific formatting, time zones), configure ICU:
# Option 1: Use the chiseled-extra image (includes ICU + tzdata)
FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-chiseled-extra
# Option 2: Disable globalization for smaller images (if not needed)
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=true
Configure .NET to respect container memory limits:
# .NET automatically detects container memory limits and adjusts GC heap size.
# Override only if needed:
ENV DOTNET_GCHeapHardLimit=0x10000000 # 256 MB hard limit
.NET automatically reads cgroup memory limits. The GC adjusts its heap size to stay within the container memory budget. Avoid setting DOTNET_GCHeapHardLimit unless you have a specific reason.
For defense-in-depth, run with a read-only root filesystem. Ensure writable paths for temp files:
ENV DOTNET_EnableDiagnostics=0
# Or mount a tmpfs at /tmp for diagnostics support
dotnet publish /t:PublishContainer for simple projects -- skip Dockerfile boilerplateUSER directive or chiseled images (non-root by default).dockerignore -- keep build context small and exclude secretsmcr.microsoft.com/dotnet/sdk as the final image -- SDK images are 800+ MB and include build tools. Always use aspnet, runtime, or runtime-deps for the final stage.10.0.1) -- use 10.0 to receive security patches. Pin to patch versions only if you have a specific compatibility requirement.HEALTHCHECK with chiseled images -- chiseled images have no curl or shell. Use orchestrator-level probes (Kubernetes httpGet, Docker Compose test) instead.--no-restore on dotnet publish after a separate dotnet restore step -- without it, restore runs again and breaks layer caching.ASPNETCORE_HTTP_PORTS, ensure the port is >= 1024..dockerignore -- without it, the build context includes .git, bin/obj, and potentially secrets, increasing build time and image size.