Upgrading from Umbraco 13 (LTS) to Umbraco 17 (LTS) is not a simple version bump. It is a upgrade that affects your framework, dependencies, search layer, and often a significant amount of custom code.
Over time, I have settled on a phased approach that combines early validation, incremental upgrades, and a strong focus on getting the website running as soon as possible. This article describes that approach and how I typically apply it in real projects.
The goal is to stay in control while moving forward steadily.
Before touching any code, I start with analysis.
First, I check which community packages support Umbraco 17. Unsupported packages are an early risk indicator and often require replacement or removal.
Next, I review all custom packages in the solution and ask a few critical questions:
Can this be replaced by a community alternative?
Does it still add real value?
Does it need to be rebuilt for Umbraco 17?
This phase sets expectations early. Decisions made here directly influence how smooth the rest of the upgrade will be.
I strongly recommend involving your customer at this stage. An upgrade is a good moment to remove logic that is no longer needed or to replace custom solutions with more maintainable alternatives.
Before upgrading the real solution, I first validate the database.
I create a temporary Umbraco 17 project and:
Restore a local copy of the database and media
Connect the temporary project to that data
Let Umbraco upgrade the database schema
Install the required community packages
Generate models using ModelsBuilder
This phase answers critical questions early:
Does the database upgrade succeed?
Do packages migrate correctly?
Are content types and properties still valid?
It is important to keep in mind that custom property editors will not work at this stage. That is expected and not a blocker. The goal here is validation, not completeness.
At the end of this phase, I have a clean Umbraco 17 database and a set of generated models that represent the upgraded schema. This provides a solid and reliable starting point for the rest of the upgrade.ent the upgraded schema and should have a good starting point.
Now the real solution upgrade begins.
I update the solution project by project, starting with the smallest projects that have the fewest dependencies. A typical order looks like this:
Core projects
Data and domain projects
Business logic
Search foundations and indexing logic
For each project, I:
Upgrade to .NET 10
Update Umbraco packages to version 17
Resolve deprecated APIs
Fix compilation errors
For the data project, where the generated models usually live, I copy the models from the temporary Umbraco 17 project into the real solution. This removes a large number of content-related compile errors early and allows dependent projects to compile much faster.
Search is explicitly included at this stage. Indexers, value extraction, and event-based indexing logic are upgraded early, so the website does not fail later due to missing or broken search dependencies.
At this point, I deliberately exclude backoffice-specific logic where necessary. The backoffice is typically the area most affected by breaking changes between Umbraco 13 and 17. Temporarily excluding this logic helps keep focus on getting the core and frontend foundations compiling first.
The goal of this phase is a stable, compiling foundation, including all search-related code, on top of which the rest of the solution can be safely upgraded.
With the core logic in place, I focus on custom property editors.
At this point:
I replace custom editors with community alternatives where possible
Only rebuild editors if no suitable alternative exists
Remove obsolete or unused editors entirely
Once property editors are fixed, I regenerate the models again.
This step is important.
Property editors directly influence generated models and value converters. Regenerating ensures:
Models reflect the final editor setup
Data and website projects compile against the correct schema
Hidden editor-related issues surface early
Skipping this step often leads to subtle runtime bugs later.
With the solution compiling, I start validating runtime logic.
In this phase:
Controllers are updated according to Umbraco 17 documentation
ViewComponents are reviewed and validated
New Umbraco services are introduced where older APIs no longer exist
Missing notifications or removed hooks are skipped instead of reimplemented immediately
Search is validated end-to-end:
Indexing runs
Queries execute
Results are returned correctly
This phase ensures the frontend logic behaves correctly on Umbraco 17.
At this point, I run the website.
This is a short but important checkpoint:
Does the site start?
Can pages be rendered?
Is the layout roughly correct?
If the website works visually and functionally at a basic level, I proceed with confidence.
Before refining the frontend, I focus on the backoffice.
This includes:
Backoffice customizations
Synchronization logic
API integrations
Middleware
Notifications and background processes
If a backoffice issue appears at any point, it takes priority over frontend issues. Editors must be able to work reliably before anything else matters.
Only after the platform is stable do I return to obsolete code.
In this phase:
Obsolete APIs are replaced with correct Umbraco 17 alternatives
Architecture is aligned with modern Umbraco patterns
Testing is structured and deliberate:
Backoffice first
Content editing
Publishing
Media handling
Custom dashboards and workflows
Notifications
Frontend second
Page rendering
Navigation
Forms
Search behavior
If a backoffice issue is found during testing, it is always fixed first. A visually perfect website is useless if editors cannot work.
Some issues I regularly see during upgrades:
Upgrading the website project before core and data projects
Leaving unsupported community packages in place “temporarily”
Delaying search upgrades until the end
Forgetting to regenerate models after fixing property editors
Spending too much time fixing frontend issues before the backoffice is stable
Avoiding these pitfalls saves time and frustration later.
This approach is intentionally generic. It is not tied to a single project, setup, or client. The same principles apply whether you are upgrading a small site or a large multi-project solution.
Key principles:
Validate the database early
Upgrade bottom-up
Include search early
Fix property editors before regenerating models
Prioritize compilability over completeness
Treat backoffice stability as the highest priority
Replace obsolete code only after the platform is stable
An Umbraco 13 to 17 upgrade does not need to be chaotic. When approached in structured phases, it becomes predictable, repeatable, and manageable across projects.
I use this approach as a baseline for every Umbraco LTS upgrade and adapt it per project where needed.