.NET

Last updated

.NET is supported by the zip flow from a prebuilt publish output — you build locally, and the platform runs the result.

Publish, then zip the output

dotnet publish -c Release -o publish

Then zip the contents of publish/, so the archive contains your .dll files and *.runtimeconfig.json at the top level. That runtimeconfig.json is what identifies your entry assembly, so it needs to be there.

Don't zip your source tree with bin/ and obj/ in it — those are large, and they're build artefacts rather than the published output.

Or bring a Dockerfile

If you'd rather build on the platform, or you want control over the SDK and runtime images, write a multi-stage Dockerfile and use git push:

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /out

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /out .
ENTRYPOINT ["dotnet", "MyApp.dll"]

Note the runtime stage uses the aspnet (or runtime) image, not the SDK — you don't need a compiler to run a published app, and it's a much smaller image, which means shorter deploys.

Watch the build memory if you go this route: the platform's builder has less RAM than your app does. If the build stops part-way with no clear error, build in CI and push the image.

Web apps must bind all interfaces

ASP.NET Core defaults to localhost, which is unreachable from outside the container. Set the URL explicitly:

ASPNETCORE_URLS=http://0.0.0.0:8080

as an environment variable, then bind that port as HTTPS. Don't configure Kestrel for HTTPS — TLS is terminated at the edge and your app should serve plain HTTP.

Set ASPNETCORE_ENVIRONMENT=Production too, unless you have a reason not to.

Runtime sizing

.NET is container-aware — it reads your memory and CPU limits rather than the host's, so you don't need the manual configuration some other runtimes require.

The garbage collector is worth one thought on a small allocation: server GC is tuned for larger machines, and workstation GC has a smaller footprint. If you see memory pressure on a modest preset, that's the knob.

Storage and configuration

  • Anything written outside a volume is discarded on the next deploy — including SQLite files and uploaded content.
  • Configuration comes from environment variables, which map onto the standard configuration providers. Nested keys use __ — ConnectionStrings__Default, for example.
  • Secrets belong in environment variables, not in appsettings.json inside the image.