Umbraco 13 to 17

Posted on July 12, 2026 in umbraco

This site has started as an Umbraco v4.x site, and has been regularly upgraded since then, never completely rebuilt. It has been running Umbraco 13 for about 3 years now, and it is about time to upgrade. We started with 17 in mind, so this post is about upgrading to 17. But 18 is out, and we are going to upgrade to it soon.

Packages

This is the easy part. The solution contains two projects: a Core project, and a UI project. Open each .csproj file, bump TargetFramework from net8.0 to net10.0, bump Umbraco package references from 13.0.2 to 17.4.0, bump a few other packages as per NuGet's suggestions.

The excellent Breaking Changes documentation tells us to remove RazorCompileOnBuild and RazorCompileOnPublish so, let us do it. It also tells us to clean our Program class a little. Remove a .AddDeliveryApi() call, remove a .UserInstallerEndpoints() calls, that is it.

At this point, rebuilding fails.

ModelsBuilderSettings.ModelsMode used to be an enum, looks like it is now a string. Quite simple to fix. Everything that queried the IPublishedContentCache does not compile anymore, porting to IPublishedContentQuery is trivial. There is no IContentService.SaveAndPublish() anymore, apparently, and one needs to Save() then Publish().

All in all, nothing dramatic.

Some of my custom Markdown property editor and converter will not compile, I am going to comment them out for the time being. And... it builds. Will it boot?

Database

Trying to boot the site fails... with a BootFailedException. A migration seems to want to delete a non-existing constraint named DF_umbracoUser_createDate. A quick Internet search shows that this happened to others as well, so something must have gone wrong with migrations at some point. Not going to troubleshoot, manually creating the missing constraints before upgrading is quicker:

 ALTER TABLE umbracoUser ADD CONSTRAINT DF_umbracoUser_createDate DEFAULT GETUTCDATE() FOR createDate;
 ALTER TABLE umbracoUser ADD CONSTRAINT DF_umbracoUser_updateDate  DEFAULT GETUTCDATE() FOR updateDate;

And presto... the site upgrades, boots, and I can log into the back-office.

Networking

At least, I can on my development machine, where I access Umbraco directly on localhost via IIS. But on production, where Umbraco runs in a Docker container behind NGINX, I get weird 400 Bad Request errors. At this point I went through a few hours of frustration and random experiments.

I sort of thought that it might have to do with forwarded headers as my issue was close to this Umbraco forum post. I added the ForwardedHeaders middleware to the app, along with the headers in NGINX configuration. Would not work. Then I left the middleware in place but reverted the NGINX configuration. It finally worked. I don't know why.

Now connecting to the back-office would fail with upstream sent too big header while reading response header from upstream errors being logged. That one was easier, it's an FAQ and applying the fix to NGINX configuration fixed it all.

At that point we have: a functional back-office, and broken sites due to...

Data Types

A range of data types are gone or have changed. And of course my custom data types are CompletelyBroken™ which is not a surprise, since the back-office UI has been entirely rebuilt.

First, native data types. Basically, go into the Data Types back-office section, and re-assign those that have issues to the proper property type. Rather simple. Weird enough, a content list datatype is throwing in the back-office, because of its orderBy value. Something a migration did not catch? Manually re-saving the datatype fixes it.

And then, I actually use two custom data types:

As far as Markdown is concerned, I was using a property editor in order to support additional custom syntax. Turns out a property editor is not needed anymore, a plain IMarkdownToHtmlConverter implementation is all that is needed. So, the property editor is gone, and it is way nicer this way.

On the other hand the image focus editor needs to be completely rebuilt.

My First Property Editor

There is some nice documentation about property editors in v17, so let us follow it.

mkdir PropertyEditors
cd PropertyEditors
npm create vite@latest ZpqrtBnk.ImageFocus
# with Lit
# with TypeScript
# without Vite RollDown (but I was not proposed it)
cd ZpqrtBnk.ImageFocus
npm i && npm install -D @umbraco-cms/backoffice@17.4.0
rm -rf public/* src/*

Then,

Is all it takes. OK, it takes a bit of time to figure out how to put everything together but hey, this is my very first time with the new UI. The end result is quite polished. As in, it makes way more sense than the convoluted Angular version.

In the end,

npm run build

Drops files in my site's App_Plugins and we are done. As in, it just works. I have to say the experience is quite pleasant.

Models

We are not entirely done with backend work. A few more things have changed, including the CLR type of objects returned by various data types. So, most of our models are broken.

The new MediaPicker3 for instance does not return a plain IPublishedContent, but a MediaWithCrops. I do not care about crops. And I had a media converter ensuring that the property value type would be reported as Image in case the picker can only pick one single image. So... that converter needs a bit of update to report the proper type, and to unwrap MediaWithCrops to Image wherever appropriate.

And then, with the various changes applied to property types, I though I would rebuild my models. Turns out they were built using the Our.ModelsBuilder edition of the Models Builder, not the built-in one, because I am using some extra features.

Well, a few more hours were spent bringing Our.ModelsBuilder to 2026 standards. It still is a huge mess, but at least it is working again. I have pushed the changes to the repository. I do not believe anyone uses it anymore, but hey. It has advanced features that Core does not provide.

Wrapping Up

At this point, I have a running back-office, clean data types, everything is nice, time to test the front-end.

And... it works.

What else can I say? Total time spent is probably 1 to 2 days of work. I like it way better than rebuilding everything, I really like that it this used to be a v4 site :) It is funny to see how many things have not changed much over the past 4-5 years, in terms of low-level concepts. On the other hand the experience is getting way more polished and user-friendly. And I love the new back-office.

The new Markdown editor does not seem fully stable, but probably it will get better with 18.

Good work, team!

There used to be Disqus-powered comments here. They got very little engagement, and I am not a big fan of Disqus. So, comments are gone. If you want to discuss this article, your best bet is to ping me on Mastodon.