Below is an example Dockerfile
that we recommend at Depot for building images for .NET Worker Service applications.
# syntax=docker/dockerfile:1
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY src/WorkerService/WorkerService.csproj src/WorkerService/
COPY *.sln ./
RUN --mount=type=cache,target=/root/.nuget/packages \
--mount=type=cache,target=/root/.local/share/NuGet/v3-cache \
--mount=type=cache,target=/root/.local/share/NuGet/plugins-cache \
--mount=type=cache,target=/tmp/NuGetScratchroot \
dotnet restore
COPY src/ src/
RUN --mount=type=cache,target=/root/.nuget/packages \
--mount=type=cache,target=/root/.local/share/NuGet/v3-cache \
--mount=type=cache,target=/root/.local/share/NuGet/plugins-cache \
--mount=type=cache,target=/tmp/NuGetScratchroot \
dotnet publish "src/WorkerService/WorkerService.csproj" \
--no-restore \
--configuration Release \
--self-contained true \
--output /app/publish \
/p:PublishSingleFile=true
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0 AS runtime
WORKDIR /app
RUN groupadd -g 1001 appgroup \
&& useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser
COPY --from=build --chown=appuser:appgroup /app/publish/WorkerService .
USER appuser
ENV DOTNET_RUNNING_IN_CONTAINER=true \
DOTNET_EnableDiagnostics=0
ENTRYPOINT ["./WorkerService"]
At a high level, here are the things we're optimizing in our Docker build for a .NET Worker Service:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY src/WorkerService/WorkerService.csproj src/WorkerService/
COPY *.sln ./
We use the .NET 8 SDK image and set up the build environment for building the Worker Service application.
RUN --mount=type=cache,target=/root/.nuget/packages \
--mount=type=cache,target=/root/.local/share/NuGet/v3-cache \
--mount=type=cache,target=/root/.local/share/NuGet/plugins-cache \
--mount=type=cache,target=/tmp/NuGetScratchroot \
dotnet restore
We restore NuGet packages with cache mounts to persist dependencies between builds, improving build performance on subsequent runs.
COPY src/ src/
RUN --mount=type=cache,target=/root/.nuget/packages \
--mount=type=cache,target=/root/.local/share/NuGet/v3-cache \
--mount=type=cache,target=/root/.local/share/NuGet/plugins-cache \
--mount=type=cache,target=/tmp/NuGetScratchroot \
dotnet publish "src/WorkerService/WorkerService.csproj" \
--no-restore \
--configuration Release \
--self-contained true \
--output /app/publish \
/p:PublishSingleFile=true
The publish command includes several important options:
--no-restore
skips package restoration since we already restored dependencies--self-contained true
includes the .NET runtime in the output/p:PublishSingleFile=true
creates a single executable file--configuration Release
builds in release mode for productionFROM mcr.microsoft.com/dotnet/runtime-deps:8.0 AS runtime
FROM mcr.microsoft.com/dotnet/runtime-deps:8.0 AS runtime
WORKDIR /app
RUN groupadd -g 1001 appgroup \
&& useradd -u 1001 -g appgroup -m -d /app -s /bin/false appuser
The runtime stage uses the runtime-deps
image, which contains only the native dependencies needed by self-contained .NET applications. We create a non-root user for security.
COPY --from=build --chown=appuser:appgroup /app/publish/WorkerService .
USER appuser
ENV DOTNET_RUNNING_IN_CONTAINER=true \
DOTNET_EnableDiagnostics=0
ENTRYPOINT ["./WorkerService"]
We copy only the single executable file from the build stage, switch to a non-root user, and configure the .NET runtime for container environments.
Self-contained deployment offers several advantages for Worker Services:
The runtime-deps
image provides only the native dependencies required by .NET:
Cache mounts are one of the most powerful features for optimizing Docker builds with Depot. This Dockerfile uses the following cache mount syntax:
RUN --mount=type=cache,target=/root/.nuget/packages \
--mount=type=cache,target=/root/.local/share/NuGet/v3-cache \
--mount=type=cache,target=/root/.local/share/NuGet/plugins-cache \
--mount=type=cache,target=/tmp/NuGetScratchroot \
dotnet restore
type=cache
: Specifies this is a cache mount that persists across builds.
Multiple NuGet cache targets:
/root/.nuget/packages
: Global NuGet package cache/root/.local/share/NuGet/v3-cache
: NuGet v3 API metadata cache/root/.local/share/NuGet/plugins-cache
: NuGet plugins cache/tmp/NuGetScratchroot
: Temporary extraction directoryFor more information regarding NuGet cache mounts, please visit the official Microsoft documentation.