Build a .NET app
A published .NET app does not need the SDK at runtime, only the matching runtime. So dotnet publish in the build stage with the SDK image, then run the published output on the slim runtime base: aspnet for web apps and APIs, dotnet-runtime for console and worker apps. The SDK and the NuGet cache stay behind in the build stage.
These build on ghcr.io/quenchworks/images/dotnet:10 (the SDK), then run on ghcr.io/quenchworks/images/aspnet:10 or ghcr.io/quenchworks/images/dotnet-runtime:10. Keep the runtime line matched to the SDK you built with.
The multi-stage Dockerfile
# Build stage: restore, then publish the app.FROM ghcr.io/quenchworks/images/dotnet:10 AS buildWORKDIR /src
# Restore first so the layer caches when only code changes.COPY ["App.csproj", "./"]RUN ["dotnet", "restore", "App.csproj"]COPY . .RUN ["dotnet", "publish", "App.csproj", "-c", "Release", "-o", "/app/publish", "--no-restore"]
# Runtime stage: ASP.NET Core runtime, nonroot.FROM ghcr.io/quenchworks/images/aspnet:10 AS runtimeWORKDIR /appENV ASPNETCORE_URLS=http://+:8080COPY --from=build /app/publish ./USER 1001EXPOSE 8080ENTRYPOINT ["dotnet", "App.dll"]# Build stage: restore, then publish the worker/console app.FROM ghcr.io/quenchworks/images/dotnet:10 AS buildWORKDIR /src
COPY ["Worker.csproj", "./"]RUN ["dotnet", "restore", "Worker.csproj"]COPY . .RUN ["dotnet", "publish", "Worker.csproj", "-c", "Release", "-o", "/app/publish", "--no-restore"]
# Runtime stage: the plain .NET runtime, no ASP.NET Core stack.FROM ghcr.io/quenchworks/images/dotnet-runtime:10 AS runtimeWORKDIR /appCOPY --from=build /app/publish ./USER 1001ENTRYPOINT ["dotnet", "Worker.dll"]What each stage does
- Build. The
dotnetimage is the full SDK. Restore before copying the rest of the source so that layer caches, thendotnet publishin Release to a fixed folder. The SDK, the NuGet cache, and the source stay in this stage. - Runtime. Start from
aspnet(for web) ordotnet-runtime(for console), copy in the publish output, drop touid 1001, and run the entry assembly. No SDK ships.
Next
- For the smallest footprint, publish a self-contained, trimmed app and run it on a runtime base, or AOT-compile a console app to a native binary and ship it on
static. - Pin by digest so each build runs exactly the base that was scanned and signed.