Skill v1.0.1
currentAutomated scan100/100+3 new
version: "1.0.1" name: docker description: > Docker containerization for .NET 10 applications. Covers multi-stage builds, .NET container images, non-root user configuration, health checks, and .dockerignore. Load this skill when containerizing an application with a Dockerfile, optimizing image size, setting up Docker Compose for local development, or when the user mentions "Docker", "Dockerfile", "container", "docker-compose", "image", "multi-stage", "non-root", ".dockerignore", or "container health check". For Dockerfile-less SDK publishing (dotnet publish /t:PublishContainer), load the container-publish skill instead.
Docker
Core Principles
- Multi-stage builds always — Separate build and runtime stages. Build in the SDK image, run in the ASP.NET runtime image.
- Non-root by default — .NET container images support
USER appby default since .NET 8. Never run as root in production. - Layer caching matters — Copy
.csprojfiles and restore before copying source code. This caches NuGet dependencies across builds. - Health probes at the orchestrator level — Expose a
/health/liveendpoint and let Kubernetes/Compose probe it. Chiseled and default aspnet images have no shell or curl, so in-imageHEALTHCHECKcommands have nothing to run with.
Patterns
Multi-Stage Dockerfile for Web API
# Stage 1: BuildFROM mcr.microsoft.com/dotnet/sdk:10.0 AS buildWORKDIR /src# Copy project files and restore (cached layer)COPY ["src/MyApp.Api/MyApp.Api.csproj", "src/MyApp.Api/"]COPY ["src/MyApp.Domain/MyApp.Domain.csproj", "src/MyApp.Domain/"]COPY ["Directory.Build.props", "."]COPY ["Directory.Packages.props", "."]RUN dotnet restore "src/MyApp.Api/MyApp.Api.csproj"# Copy everything and buildCOPY . .RUN dotnet publish "src/MyApp.Api/MyApp.Api.csproj" \-c Release \-o /app/publish \--no-restore# Stage 2: RuntimeFROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtimeWORKDIR /app# Non-root user (default in .NET 8+ images)USER appCOPY --from=build /app/publish .EXPOSE 8080ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
Container Health Probes
Prefer orchestrator-level probes (Kubernetes livenessProbe, Compose healthcheck) over a Dockerfile HEALTHCHECK — the standard aspnet and chiseled images ship no shell, no curl, and no wget, so there is nothing inside the container to run the probe with. Point the orchestrator at /health/live:
# docker-compose — probe from outside the app processservices:api:healthcheck:test: ["CMD-SHELL", "wget -qO- http://localhost:8080/health/live || exit 1"]interval: 30stimeout: 3sretries: 3# Note: CMD-SHELL requires a shell + wget in the image. Use a non-chiseled# variant for this, or better: let Kubernetes httpGet probes do it —# they run from the kubelet, needing nothing inside the image.
If you must have an in-image HEALTHCHECK, base the runtime stage on a non-chiseled image that includes wget — never re-run the app binary as the probe command; that starts a second instance instead of checking the first.
.dockerignore
**/.git**/.vs**/bin**/obj**/node_modules**/Dockerfile***/docker-compose***/tests
Docker Compose for Local Development
Key .NET-specific concerns — pass connection strings via environment, use depends_on with health checks:
services:api:build:context: .dockerfile: src/MyApp.Api/Dockerfileports:- "5000:8080"environment:- ASPNETCORE_ENVIRONMENT=Development- ConnectionStrings__Default=Host=postgres;Database=myapp;Username=postgres;Password=postgres- ConnectionStrings__Redis=redis:6379depends_on:postgres:condition: service_healthy# Add postgres/redis services with healthcheck — standard boilerplate
Optimized Build with .slnx
For solutions with multiple projects, restore only the necessary projects.
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS buildWORKDIR /src# Copy solution and all project filesCOPY *.slnx .COPY Directory.Build.props .COPY Directory.Packages.props .COPY src/**/*.csproj ./src/# Restore project structureRUN for file in src/**/*.csproj; do \mkdir -p $(dirname $file) && mv $file $(dirname $file)/; \doneRUN dotnet restoreCOPY . .RUN dotnet publish src/MyApp.Api -c Release -o /app/publish --no-restore
Health Check Endpoint
// In Program.cs — lightweight health endpoint for Dockerapp.MapGet("/health/live", () => Results.Ok("healthy")).ExcludeFromDescription();
Anti-patterns
Don't Use SDK Image for Runtime
# BAD — SDK image is 900MB+, includes compilersFROM mcr.microsoft.com/dotnet/sdk:10.0COPY . .RUN dotnet run# GOOD — separate build and runtime, runtime image is ~200MBFROM mcr.microsoft.com/dotnet/aspnet:10.0
Don't Copy Everything Before Restore
# BAD — any source change invalidates the NuGet cacheCOPY . .RUN dotnet restore# GOOD — copy only project files first, then restoreCOPY ["src/MyApp.Api/MyApp.Api.csproj", "src/MyApp.Api/"]RUN dotnet restore "src/MyApp.Api/MyApp.Api.csproj"COPY . .
Don't Run as Root
# BAD — running as root (security risk)FROM mcr.microsoft.com/dotnet/aspnet:10.0COPY --from=build /app .ENTRYPOINT ["dotnet", "MyApp.Api.dll"]# GOOD — use the built-in non-root userFROM mcr.microsoft.com/dotnet/aspnet:10.0USER appCOPY --from=build /app .ENTRYPOINT ["dotnet", "MyApp.Api.dll"]
Decision Guide
| Scenario | Recommendation | |
|---|---|---|
| Web API container | Multi-stage build with aspnet runtime image | |
| Worker service | Multi-stage build with dotnet/runtime image | |
| Local development | Docker Compose with service dependencies | |
| CI builds | Multi-stage build (self-contained) | |
| Image size optimization | Use Alpine variant + trimming for small images | |
| Health monitoring | /health endpoint + orchestrator probe (K8s httpGet / Compose healthcheck) | |
| Secrets | Environment variables or mounted secrets, never in image |