Cropped image showing a monitor and a Raspberry Pi. The monitor shows the Raspberry Pi OS desktop with a browser running Umbraco 13.

Seeing out the year with .net 8 and Umbraco 13

After many months of teasing us with release candidates, the final latest release of Microsoft’s .net 8 framework came out mid-November, marking the latest long-term-support release of .net core (for all we’re not supposed to include the ‘core’ moniker any more). Following the new roadmap plan of aligning their own LTS releases to that of the framework, Umbraco 13 which was based on top of the new framework followed very shortly afterwards in mid December. People who have followed some of my recent blog posts will know I’ve been looking at potentially migrating an internal site onto the latest version of Umbraco as well as moving it from Windows hosting to Linux, but had decided it made more sense to wait for all the LTS releases to come out first. For one thing, big performance improvements in the updated framework from Microsoft have been touted many times, and who wouldn’t want to eke every last bit out of their existing hosting?

Installing .net 8 on ARM platforms

Now that all of this is available, I decided it was time to start looking at this in earnest to finish off 2023. Unfortunately this was far from plain sailing, so this blog post serves as a bit of a discovery exercise for anyone else who may find it useful. The biggest initial stumbling block actually came with installing .net 8 itself. The simplest way to install .net on Linux normally is to pull it down from apt (I know other package managers are available, but I’m not the BBC so I don’t have to offer equal prominence here). Ubuntu-based operating systems often have the .net SDK and runtimes available in their own internal package feed, or at least that was the case with .net 6 and the intervening short term release .net 7. As of the time of writing this at the end of December though, .net 8 is still only available on the latest short-term-support version of Ubuntu, namely 23.10, and importantly not on their own latest long-term-support release Ubuntu 22.04, with no firm indication it will ever be backported. [Addendum March 2024 – This was finally backported to the repo for Ubuntu 22.04 in February 2024, meaning the following steps are usually no longer needed on this OS and it should be obtainable as an apt package]. This can sometimes be gotten around by adding Microsoft’s package repo to apt in addition to the OS native one. But the Microsoft package repo only supports the x64 architecture, and not other architectures like ARM which the Ubuntu provided builds do cover. This is a big blocker when using an ARM-based platform like the Raspberry Pi or an Oracle Linux VM, both of which are platforms you may have read I’ve been recently experimenting with Umbraco on.

In this situation a more manual installation process can be followed rather than being able to depend on the package. Microsoft do also provide a script solution for this and documentation on their site on how to use it, however it is a little more fiddly with some less-than-clear parts further in for non-Linux-experts like myself, so the following is a more condensed walkthrough of the steps involved to do this.

Important note: The following all assumes you do not already have older versions of .net installed via packages. If you do, then the path for these will take precedence and the script instructions below will not work to make the new version available globally and instead it’ll only work within the locally installed folder. In this case, you can either choose to only run it locally within that folder, or to run it globally it is possible to instruct the installer script to install into the same folder as the existing dotnet versions using an additional –install-dir (that’s double dash at the start) switch on the installer script as root. This will usually be ‘/usr/share/dotnet/’ or ‘/usr/lib/dotnet/’; the ‘dotnet –list-runtimes’ command should tell you where your existing ones are. However it’s important to caveat that mixing versions of dotnet installed via packages and via the script is certainly not recommended as it can cause confusion if you need to reinstall/update/remove versions later!

1) Pull down the Linux script from Microsoft and save it to your local home directory. This can be done from a terminal using the command:-
wget https://dot.net/v1/dotnet-install.sh
2) Make the downloaded script executable. This can be done using the command:-
chmod +x ./dotnet-install.sh
3) Run the installer script, with the options to get the latest version of the aspnetcore runtime. I am not building anything this on this machine so the full SDK isn’t needed:-
./dotnet-install.sh --version latest --runtime aspnetcore

This downloads and installs the .net 8 runtime for running sites, but unlike the package approach by default it’s only put in a local folder under your user and is not executable from outside of this folder. This is where I found the Microsoft guide became a little less clear as it lists instructions for several different terminal environments and requires you to jump to the end of their documentation for it. In most standard Ubuntu/Debian based installs, you’re likely to be using bash for terminal access. Others are available which is why MS keep it quite open in their guide, but if you were using a different one you’re probably more of an expert in Linux already so would likely know this. So if I focus only on adding this in for the common bash here:-

1) Run the text editor command with the .bashrc file with the command:-
nano ~/.bashrc
2) This file will very likely already have lots of lines in, so if it’s empty/new this may be an indication you’re not using it. Assuming it does have code in, the following two lines (and an optional comment) can be added to the end:-

## Dotnet Scripts
export DOTNET_ROOT=$HOME/.dotnet
export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools

3) Save the file and exit using CTRL-X (yes, unlike most tools, this is not the shortcut for ‘cut’ in the Nano text editor)

Assuming everything has worked correctly (a restart either of the whole machine or at least just the terminal may be needed) the dotnet command should now execute globally without needing to be in a particular folder. After that, running your Umbraco site is much the same as it was when ran via dotnet installed as a package and can be done using the usual dotnet x.dll command.

Performance

With the framework up and running and able to serve websites, it was at this point I wanted to explore a little more around performance. Every time a new version of .net comes along, Microsoft understandably like to put out lots of blog posts and statistics around how much faster and more performant the new iteration of the framework will be, and true to form .net 8 has seen many of the same things over recent months with some amazing stats coming out. However like all arbitrary benchmarks, I’m always a bit more skeptical and keen to see what a difference they will make in a real application.

The full plan is to eventually re-run all of the 5 tests on the same hardware stack as the last post, just now using an Umbraco instance based on .net 8 and Umbraco 13. But as a quick test just for the sakes of this I decided to run only one of the tests on a single platform. The most logical choice to try and highlight any difference was to pick the slowest test from the previous test suite and run it on the slowest platform. To go about this, I upgraded the same BaselineSite application put together previously to Umbraco 13 and .net 8 and rebuilt everything, without any other changes being made to the area of c# code which generates 1500 nodes using Umbraco’s ContentService. This updated application was then moved onto the Raspberry Pi 3B+ with a copy of the new .net 8 framework installed following the above guide, and re-run three times with an average time taken. The time comparison between the two gave the following results:-

  • .net 7/Umbraco 12 – 15m03
  • .net 8/Umbraco 13 – 14m36

So there is a small improvement of around 3% can be seen when looking at the averaged times in this particular test. However the times are still so close I’d say it’s hard to conclusively say there’s a huge difference and this isn’t just something that could have been attributed to timing differences that could be seen just with runs on the same version (my original Umbraco 12 test runs varied from 14m45 to 15m15 for example). As I say I do intend to dive into this more early in 2024 to try and establish more detail on this. But as an initial first line check it largely seems to confirm my suspicions that, while I’m sure there are numerous under-the-hood improvements in the performance of the main framework, they’ll likely be in very specific code paths and your particular Umbraco usage may not really see them. So if you expect to just use exactly the same code with no changes and see blistering changes in speed everywhere after having read many months of .net 8 performance boasts, then you probably won’t see these expectations met. However if you’re being a bit more realistic, these early tests do seem to show that even if you changed nothing else you are going to be gaining lots of new features that come with the latest Umbraco version with possible tiny speed improvements and certainly no slowdowns in the performance. So there’d really be very little reason not to go ahead and perform the upgrade from .net 7/Umbraco 12 to .net 8/Umbraco 13. You may see even more pronounced improvements if you were sticking to LTS-only releases so jumping all the way from .net 6/Umbraco 10 to .net 8/Umbraco 13.

And with that, that’s me done for 2023. So if you’ve made it to the end reading this too let me wish all of you a Happy New Year 2024.