Short answer. For the custom code, go straight from 18 to 20. Porting once to 20 meets 44,778 verified changes in Odoo core (Community Edition, including 8,630 in country localisation modules). Porting twice, first to 19 and then to 20, meets 25,018 and then 32,680, 57,698 in total, so the direct route carries 22% fewer changes than the two ports combined and saves a second test cycle and a second go-live. The two-step path only wins when something outside the code forces it: a Community database that OpenUpgrade must walk through 19 anyway, or a business that needs a feature of 19 before 20 is ready for it. In both cases the code should still be ported once, to 20.
Why the question comes up now
Odoo 18 shipped in October 2024 and Odoo's support policy gives each major version three years, so 18 databases have until late 2027. Odoo 20 shipped on 24 September 2026. A lot of teams sat out 19, either deliberately or because the project never got scheduled, and now face a two-version gap with the end of support in sight. The question they ask partners is whether the gap should be closed in one project or two.
The database half of the answer is settled by edition. On Enterprise, Odoo Online and Odoo.sh, Odoo's upgrade service takes a database from 18.0 to 20.0 in one run. On Community, OpenUpgrade moves one major version at a time, so the database passes through 19 whatever you decide about the code. That leaves the custom-code half, which is where the money goes, and where the numbers below apply.
What each leg carries
Counted the same way on all three legs: changes in Odoo core that custom code can depend on, Python methods and fields, XML views and records, JavaScript modules, Community Edition.
| Leg | Verified changes | Core modules | Models |
|---|---|---|---|
| 18 → 19 | 25,018 | 369 | 1,486 |
| 19 → 20 | 32,680 | 408 | 1,566 |
| Two ports, added up | 57,698 | ||
| 18 → 20 direct | 44,778 | 445 | 1,923 |
Three things stand out.
The direct jump is not the sum of its parts. Many changes made in 19 were changed again or reverted in 20, and a field renamed in 19 and removed in 20 is one change on the direct path and two on the stepped path. Direct comes to 78% of the two legs combined.
But it is a bigger project than a single step. 44,778 against 32,680 is 37% more than a team moving from 19 would face, and the increase shows in every business area, from 27% in Website & eCommerce to 56% in Marketing & Events. A partner who prices an 18 → 20 upgrade from a 19 → 20 reference will under-quote.
Localisation rows are part of every headline. The 18 → 20 total includes 8,630 changes in country localisation modules, which the per-area figures below leave out. If your custom code does not touch a localisation, those rows are not your problem.
By business area
Changes in core modules per area, Community Edition, localisation and theme modules excluded:
| Area | 18 → 19 | 19 → 20 | Two ports | 18 → 20 direct | Direct vs two ports |
|---|---|---|---|---|---|
| Platform & Framework | 3,357 | 4,038 | 7,395 | 5,703 | 77% |
| Website & eCommerce | 2,762 | 3,819 | 6,581 | 4,838 | 74% |
| HR & Payroll | 2,749 | 3,120 | 5,869 | 4,189 | 71% |
| Accounting | 2,669 | 2,509 | 5,178 | 3,871 | 75% |
| Point of Sale | 1,679 | 1,817 | 3,496 | 2,615 | 75% |
| Inventory | 1,508 | 1,645 | 3,153 | 2,487 | 79% |
| Sales | 820 | 1,252 | 2,072 | 1,701 | 82% |
| Marketing & Events | 926 | 884 | 1,810 | 1,379 | 76% |
| Manufacturing | 642 | 865 | 1,507 | 1,224 | 81% |
| Project | 653 | 775 | 1,428 | 1,115 | 78% |
| Purchase | 531 | 509 | 1,040 | 791 | 76% |
| CRM | 229 | 309 | 538 | 417 | 78% |
The saving from going direct is largest where 19 churned the most and 20 churned again: HR & Payroll and Website & eCommerce. It is smallest in Sales and Manufacturing, where the two releases touched different things and little cancels out. Either way the direct path never costs more than the two ports added together, in any area.
The table counts changes in Odoo. A two-step project also pays for things that scale with the number of go-lives, not with the number of changes:
- Two test cycles with real users, two freezes of the source database, two cut-over windows, two rounds of training on changed screens.
- A second port of every module six to twelve months after the first, by which time the people who did the first one may have moved on.
- Two sets of integration re-tests. Connectors, EDI and payment providers are checked against 19, then against 20.
- An intermediate state nobody wanted. The business runs on 19 for a few months, learns its screens, and then changes again.
Against that, the stepped path has one real advantage: each step is smaller, so each test cycle finds fewer defects at once. For a team with little custom code and a low tolerance for a big-bang change, that can be worth the second go-live. For a team with a lot of custom code, it rarely is.
When to go through 19 anyway
- Community Edition with OpenUpgrade. The database goes 18 → 19 → 20 regardless. Port the code once to 20, run the database through both OpenUpgrade steps on a copy, and go live once on 20. Do not run the business on 19 in between unless you must.
- A feature or a localisation you need from 19 before 20 is mature enough for you. Odoo 20 is four weeks old at the time of writing; early point releases move fast. If the client cannot absorb a hotfix cadence, going live on 19 now and 20 in a year is a legitimate plan. Price it as two projects.
- A partner contract that prices per version. Some maintenance contracts cover one version step per year. Check before assuming the direct path is in scope.
How to decide, in four steps
- Edition first. Enterprise, Online or Odoo.sh: the database can go direct. Community: the database goes through 19; only the code decision remains.
- Count, don't guess. Get the breaking-change count per custom module for 18 → 20 and, if you are weighing the stepped path, for 18 → 19 and 19 → 20 separately. A scan of each module is free and takes minutes; the difference between the two numbers is your answer in hours.
- Price the second go-live. Add the two-cycle costs above to the stepped path. They are usually larger than the code difference.
- Pick the date from the support window, not the version. Odoo 18 support ends in 2027; plan the single go-live on 20 for a quiet quarter before then, with the first point releases of 20 behind you.
The 18 → 20 guide lists the changes that need the closest look on the direct path, with before and after code, and the 19 → 20 guide does the same for the second leg if you take it. The upgrade checklist covers both halves of either project. For a ballpark before any of that, the cost calculator prices a jump from your module sizes.
Scan a module for Odoo 20, free, no card
FAQ
Is it cheaper to upgrade Odoo 18 to 20 directly or through 19? Directly, for the custom code. The direct path carries 44,778 verified core changes against 57,698 for the two ports added up, and it needs one test cycle and one go-live instead of two. The stepped path is only cheaper when the second go-live is nearly free, which is rare.
Does Odoo's upgrade service support 18 to 20 in one run? Yes, for Enterprise, Odoo Online and Odoo.sh databases. Community databases use OpenUpgrade, which moves one major version at a time.
How much more work is 18 → 20 than 19 → 20? About 37% more changes in core, 44,778 against 32,680, and more in every business area. Price it as a bigger project than a single step, not as a single step with a surcharge.
Can I port my custom modules straight to 20 while the database goes through 19? Yes. The database path and the code path are independent. Port once to 20, run OpenUpgrade on a copy through 19 to 20, install the ported modules on the result, and test there.
Should I wait for a later Odoo 20 point release? If your team can apply hotfixes quickly, no. If the client cannot tolerate a fast patch cadence for a few months, build against 20 now and plan go-live for a later point release, keeping the single go-live.
Are these numbers Enterprise too? No. All figures are Community Edition. Enterprise adds modules and changes that are not in these counts; the Enterprise 18 → 20 total is roughly double.