Publishing apps as OCI images
November 12, 2025 ยท View on GitHub
This document demonstrates how to publish .NET console apps as container images. The workflows for ASP.NET Core apps are largely the same. These instructions are part of a container workshop, which details fundamental workflows for using .NET in containers.
Related:
Hello dotnet
The easiest way to publish an app to a container image is with .NET SDK OCI image publish. Console apps require installing a NuGet package to use OCI publish. The NuGet package isn't required for ASP.NET Core apps.
Create the app.
$ dotnet new console -n hello-dotnet
$ cd hello-dotnet
$ dotnet run
Hello, World!
Change Program.cs (so that it prints the name of the operating system):
using System.Runtime.InteropServices;
Console.WriteLine($"Hello, {RuntimeInformation.OSDescription} on {RuntimeInformation.OSArchitecture}!");
Re-run:
$ dotnet run
Hello, Ubuntu 24.04.3 LTS on X64!
Your operating system will likely be different.
Publish and run app image.
$ dotnet publish -t:PublishContainer
$ docker run --rm hello-dotnet
Hello, Ubuntu 24.04.3 LTS on X64!
Inspect the image:
$ docker images hello-dotnet
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-dotnet latest 53444c11ae48 About a minute ago 203MB
It is straightforward to create smaller images using Alpine, Ubuntu Chiseled, and various .NET SDK publishing options.
Default image configuration
Summary:
- Ubuntu is used for default base images starting with .NET 10.
- .NET 8+ images come with a new user, app.
dotnet publishsets the user toapp.- The user is set via UID, as explained in Running non-root .NET containers with Kubernetes.
Images are configured to use a non-root user, following secure by default principles.
$ docker run --rm --entrypoint bash hello-dotnet -c "cat /etc/os-release | head -n 1"
PRETTY_NAME="Ubuntu 24.04.3 LTS"
$ docker run --rm --entrypoint bash hello-dotnet -c "whoami"
app
$ docker run --rm --entrypoint bash hello-dotnet -c "cat /etc/passwd | grep app"
app:x:1654:1654::/home/app:/bin/sh
$ docker inspect hello-dotnet | grep User
"User": "1654",
ContainerUser=root can be used to configure images to use the root user.
Audit the image, with anchore/syft.
$ docker run --rm anchore/syft mcr.microsoft.com/dotnet/runtime:8.0 | grep dotnet | wc -l
1
$ docker run --rm anchore/syft mcr.microsoft.com/dotnet/runtime:8.0 | grep deb | wc -l
92
There are 1 .NET and 92 .deb packages/libraries installed.