When Microsoft drops a new .NET release, most developers on Ubuntu experience it as a simple apt install. What actually happens between that new GitHub release and your terminal is a different story entirely.

.NET is open source. When Microsoft publishes a new release or preview, the source code lands on GitHub. From there, my job as the .NET toolchain maintainer for Ubuntu starts: build the source package, upload it to a PPA for revision, run the package’s autopkgtests, and once everything passes, upload it to the Ubuntu archive where it gets built and distributed to users.

For most releases, that’s the whole story. For a brand new major version, there’s a problem.

The Chicken-and-Egg Problem

.NET needs .NET to build.

The SDK, the compiler, and many other .NET components are written in C#, so you need an existing .NET SDK to build the next one. For a point release like 10.0.x, that’s fine, we already have .NET 10 in the distro. But .NET 11 is a brand new major version. It doesn’t exist in Ubuntu yet. There’s no 11 SDK anywhere in the archive to bootstrap from.

That’s what bootstrapping means here: breaking the cycle to produce that first build.

The Two-Stage Dance (amd64 and arm64)

For amd64 and arm64, Microsoft publishes prebuilt .NET 11 SDKs, which gives us a way in.

It works in two stages:

Stage 1 bundles Microsoft’s prebuilt SDK inside the Debian source package to break the cycle. It compiles .NET 11 from source and produces a working SDK. That result gets published to a PPA (not the Ubuntu archive itself), where it can serve as a build dependency.

Stage 2 is the archive-ready version. Its source package is clean: no vendored SDK, just a declared build dependency on Stage 1’s output. It builds the same .NET 11 from the same source, producing essentially the same installable packages.

Why two stages if the output is the same? The Ubuntu archive receives source packages, builds them, and distributes the resulting binaries. A source package that vendors binaries, such as a prebuilt Microsoft SDK, doesn’t meet archive policy, regardless of how clean its output is. Stage 2 exists to produce a source package that meets that policy: one where .NET is a proper build dependency, not a vendored blob.

From PPA to Archive: Why Previews Wait for RC1

Even the clean Stage 2 package doesn’t go straight into the Ubuntu archive during the preview phase. It lives in a PPA for the entire preview cycle and only graduates to the archive around RC1.

The reason is bootstrapping, again. One preview building successfully doesn’t mean it can build the next, as the product may start using new C# language features that the previous SDK doesn’t support yet. When that happens, you go back to square one: fresh prebuilt from Microsoft, full two-stage process.

By RC1, the bar for new language features or breaking changes is high enough that rebootstraps are no longer expected. The product is stabilizing and it’s finally safe to put the package in the archive.

The Hard Path: s390x and ppc64el

Most .NET developers spend their entire careers on amd64 or arm64 and never think much beyond that. But Ubuntu ships .NET for two more architectures: s390x and ppc64el. s390x is IBM Z, the mainframe platform that quietly runs much of the world’s banking and enterprise infrastructure. ppc64el is IBM POWER, another IBM server architecture with a smaller but genuine .NET footprint in enterprise deployments. Not a huge .NET user base, but real ones, and worth supporting.

Microsoft doesn’t publish prebuilt SDKs for either, so the two-stage approach breaks down at Stage 1. There’s no prebuilt to bootstrap from.

The solution is cross-compilation. We build a .NET SDK for these architectures inside a Docker container on an amd64 machine, using a prereqs image from Microsoft that ships with the target architecture’s root filesystem already baked in. The .NET build system takes it from there: the managed code is compiled for the target architecture, and the runtime’s native C/C++ components are cross-compiled for it as well.

CoreCLR doesn’t support s390x or ppc64el yet, so the resulting .NET SDK ships the Mono runtime on these architectures instead, which is what makes it viable on them at all.

That SDK gets baked into the Stage 1 bootstrap source package for these architectures, and from that point the process is the same as for amd64 and arm64: Stage 1 lands in a PPA, Stage 2 uses it as a build dependency, producing the clean, archive-ready package.

Kicking Off with Preview 4

Preview 4 was the first .NET 11 build to go through all of this on Ubuntu, and it went smoothly. No issues across any of the four architectures, and the tooling is mature enough that the cross-compilation path needed no special handling.

That’s not always the case, and breakage doesn’t always come from .NET itself. When Ubuntu 26.04 moved to a Rust-based coreutils implementation, .NET 8 briefly stopped building because the new comm command behaved differently. The fix was shipping a patch in the source package to replace that command’s usage. Once diagnosed, the fix was simple, but it shows just how much the packaging environment can catch you off guard, independently of .NET.

Try It Out

Want to try the latest .NET 11 preview on Ubuntu? You can add the Previews PPA and install it alongside any other .NET version you already have:

sudo add-apt-repository ppa:dotnet/previews
sudo apt install dotnet11

.NET packages on Ubuntu coexist cleanly, so .NET 11 will sit alongside .NET 10, 9, 8 with no conflicts. Install what you need and the SDK handles the rest, automatically building against the right runtime based on what each project targets.

It’s a preview, though: great for exploring new language and runtime features, but not for production.

For most .NET developers on Ubuntu, none of this is visible. And it shouldn’t be. The measure of success is a package that installs cleanly, builds your project, and gets out of the way. Preview 4 does that. The rest is implementation detail.

More previews are on the way, and each one will land in the Previews PPA as it clears the pipeline, through the RC cycle and on to the November GA. If you want to follow the .NET 11 journey from the ground up and kick the tires on each release as it comes, that’s exactly what the PPA is there for. Hope to see you there.