Backups, upgrades and operations

How to upgrade Odoo 17 to 18 without losing a weekend

How to upgrade Odoo 17 to 18 for Enterprise and Community, port custom modules to the 18 API, rehearse on a copy, and plan the cutover and rollback.

CICDoo Engineering Updated 8 min read

The short answer

To upgrade Odoo 17 to 18, migrate the database with Odoo's upgrade service (Enterprise) or OpenUpgrade (Community), port your custom modules to the 18 API (tree views become list, name_get and user_has_groups are gone, group_operator becomes aggregator), and rehearse the whole run on a copy of production until it is boring. Keep the untouched 17 database and filestore as your rollback.

On this page
  1. The short answer
  2. Before you start
  3. Option 1: Odoo's upgrade service (Enterprise)
  4. Option 2: OpenUpgrade (Community)
  5. Porting custom modules: what changed from 17 to 18
  6. Rehearse on a copy until it is boring
  7. Cutover plan
  8. Rollback plan
  9. Upgrade checklist
  10. Doing this with CICDoo

The short answer

An Odoo major upgrade has two independent halves, and you need both:

  1. The database. The standard modules' data and schema must be migrated from 17 to 18. For Enterprise, Odoo does this through its upgrade service. For Community, the usual route is the OCA's OpenUpgrade project.
  2. Your code. Every custom and third-party module must be ported to the Odoo 18 API, and its version bumped to 18.0.x.y.z. Nobody does this for you unless you pay someone to.

You then rehearse the combination on a copy of production, fix what breaks, repeat until a full run passes cleanly, and only then schedule the real cutover. Budget most of the time for rehearsal and testing, not for the migration command itself.

Before you start

Check the platform first. Odoo 18 needs Python 3.10 or newer and PostgreSQL 12 or newer (15 or 16 is a sensible choice). If your 17 server runs an older PostgreSQL, upgrading PostgreSQL is a separate project; do it before or after, not during the Odoo upgrade.

Then inventory what you run:

  • Standard modules installed. Export the list from Apps, or query ir_module_module where state = 'installed'.
  • Custom modules. Yours, with owners.
  • Third-party modules. OCA and App Store modules. Check each one has an 18.0 release. A missing port is the most common blocker, and the answer is either port it, replace it, or uninstall it on 17 first.
  • Studio customisations (Enterprise). These live in the database and are migrated by the upgrade service, but review them after the rehearsal.
  • Integrations. Anything calling Odoo over XML-RPC or JSON-RPC, and anything Odoo calls. Field and model renames break these silently.

Uninstall modules you no longer use while you are still on 17. Every module you carry across is one more thing to test.

Option 1: Odoo's upgrade service (Enterprise)

If you have an Enterprise subscription, Odoo migrates the standard modules for you. For self-hosted databases, you run their script against a dump:

# Request a test upgrade: uploads the database, returns an upgraded dump
python3 <(curl -s https://upgrade.odoo.com/upgrade) test -d mydb -t 18.0

# When you are ready for the real one
python3 <(curl -s https://upgrade.odoo.com/upgrade) production -d mydb -t 18.0

The test request gives you an upgraded copy to rehearse on. The production request is the one you run at cutover. You can also upload a dump through the web form at upgrade.odoo.com. Databases on Odoo Online and Odoo.sh go through their own upgrade flows in the Odoo interface.

What the service does not do: port your custom modules. Custom code is migrated by Odoo only under a separate paid arrangement. Otherwise, your modules must be ready for 18 and present on the addons path when you start the upgraded database, and any data migration they need is your migration scripts' job. Read the official upgrade documentation for your exact case (odoo.com/documentation/18.0/administration/upgrade.html) because the process and requirements change between versions.

Option 2: OpenUpgrade (Community)

The upgrade service is not available for Community databases. The OCA's OpenUpgrade project provides migration scripts for standard Community modules, run with a patched Odoo tree:

  1. Check the module coverage list for the 18.0 branch in the OpenUpgrade documentation. Any module marked as not covered needs its own migration work, or has to be uninstalled first.
  2. Check out OpenUpgrade 18.0 alongside Odoo 18.0, and install openupgradelib.
  3. Run the upgrade against a copy of the database:
python odoo/odoo-bin -c openupgrade.conf -d mydb_copy \
  --upgrade-path=OpenUpgrade/openupgrade_scripts/scripts \
  --load=base,web,openupgrade_framework \
  --update all --stop-after-init

The exact flags are documented in the OpenUpgrade README for each branch, check them there before you run it. OpenUpgrade is volunteer maintained: coverage and quality are good for common modules, thinner for niche ones. Expect to write or fix some scripts, and consider contributing them back.

Community databases can only step one major version at a time with OpenUpgrade. From 17 to 18 is one step, which is the easy case.

Porting custom modules: what changed from 17 to 18

The changes below are from the ORM changelog and the Odoo 18 source. They are the ones most custom modules hit. For the complete list, check the official upgrade notes and changelog for 18.0.

Area Odoo 17 Odoo 18
List views <tree> and view mode tree <list> and view mode list
Record names name_get() deprecated name_get() removed; compute display_name in _compute_display_name
Name search override _name_search implement _search_display_name
Field aggregation group_operator= aggregator= (old name still accepted with a deprecation warning)
Group checks self.user_has_groups('base.group_user') self.env.user.has_group('base.group_user')
Access checks check_access_rights plus check_access_rule check_access, has_access, _filtered_access combine both
Scheduled actions numbercall and doall on ir.cron fields removed; drop them from your cron XML
Form chatter <div class="oe_chatter"> with fields the <chatter/> element

Also carried over from 17, if you skipped straight here from an older version: attrs and states in views were removed in 17 in favour of direct invisible, readonly and required expressions, and _read_group got a new signature in 17 (grouped values and aggregates as tuples). Public read_group still works in 18; it is deprecated from 19 in favour of _read_group and formatted_read_group, so new code should use _read_group server side.

Let Odoo do the mechanical part

Odoo 18 ships a code rewriter, odoo-bin upgrade_code, which applies the scripts in odoo/upgrade_code/. For 17 to 18 it renames tree to list across XML, Python and JavaScript:

# See what would change first
python odoo/odoo-bin upgrade_code --addons-path=./addons --from 17.0 --dry-run
# Then rewrite
python odoo/odoo-bin upgrade_code --addons-path=./addons --from 17.0

The OCA's odoo-module-migrator tool does similar best-effort rewriting for many version pairs. Both are starting points: review the diff, then fix the rest by hand.

A porting routine per module

  1. Create an 18.0 branch from your 17.0 branch.
  2. Run upgrade_code, commit the result separately so reviewers can skip it.
  3. Bump the version in __manifest__.py to 18.0.1.0.0 (or keep your minor numbers under the new series).
  4. Fix the API changes in the table above. Grep is your friend: name_get, _name_search, group_operator, user_has_groups, numbercall, oe_chatter.
  5. Install the module on a fresh 18 database with --test-enable and fix every error and warning. Deprecation warnings in the log are the next version's errors.
  6. If fields or models were renamed, add a migration script under migrations/18.0.1.0.0/ so data follows the rename. Odoo's upgrade-util library has helpers like rename_field and rename_model that also fix views, filters and translations.

JavaScript and OWL components need their own pass. Frontend changes between 17 and 18 depend heavily on which components you extend; test every custom widget in the browser and check the official notes for the components you patch.

Rehearse on a copy until it is boring

The rehearsal is the upgrade. The cutover is just the last rehearsal.

  1. Take a fresh production backup: database dump plus filestore.
  2. Restore it on a separate server or database, never on the production host's live database.
  3. Neutralise it (odoo-bin neutralize -d mydb_copy) so it cannot email customers or run payment and bank sync jobs.
  4. Run the database migration (upgrade service test request, or OpenUpgrade).
  5. Start Odoo 18 with your ported modules and run -u on your custom modules.
  6. Read the whole log. Every traceback and every WARNING about missing fields or views is a bug to fix.
  7. Hand the copy to key users with a test script: create a quote, confirm it, deliver it, invoice it, register the payment, run the month-end reports they actually use.
  8. Time the run. That number is your downtime estimate.

Repeat after every fix. Two or three clean rehearsals in a row is a good bar before you book the date.

Cutover plan

A cutover runbook for a self-hosted database, written down and owned by one person:

  1. Announce the freeze. Tell users the window, and that anything entered after the freeze starts is lost.
  2. Stop Odoo 17 and any integrations writing to it. Disable crons if something else might start it.
  3. Final backup. Database dump and filestore, copied off the server and checked (can you list the dump, is the filestore the expected size).
  4. Migrate. Upgrade service production request, or OpenUpgrade run, on this final dump.
  5. Deploy Odoo 18 with the ported modules, restore the upgraded database, run -u on custom modules, start the service.
  6. Smoke test with the same script as the rehearsal, by named people.
  7. Go or no-go at a fixed time. If it is not good by then, you roll back, no heroics.
  8. Reopen to users, re-enable integrations, and watch the logs closely for the first working day.

Keep the reverse proxy configuration in mind: Odoo 17 and 18 both serve the websocket at /websocket on the gevent port (8072 by default), so an existing nginx config normally carries over unchanged.

Rollback plan

Before step 4 of the cutover, the rollback is trivial: start Odoo 17 again on the untouched database. After it, rollback means restoring the step 3 backup and the 17 code, and discarding anything done on 18.

Make that possible:

  • Keep the 17 server, virtualenv and code available until the upgrade has been in production for a few weeks.
  • Keep the final pre-upgrade dump and filestore in at least two places.
  • Decide in advance how long rollback remains an option. After a few days of real transactions on 18, going back means re-keying data, and fixing forward is usually cheaper.

Upgrade checklist

  • [ ] Python 3.10+ and PostgreSQL 12+ on the target server
  • [ ] Every third-party module has an 18.0 version, or is removed on 17
  • [ ] Custom modules ported, versions bumped to 18.0, tests passing on a fresh 18 database
  • [ ] Integrations checked for renamed fields and models
  • [ ] At least two clean rehearsals on a neutralised copy, with timing
  • [ ] Key-user sign-off on the rehearsal copy
  • [ ] Written cutover runbook with a go or no-go time
  • [ ] Pre-upgrade backup stored in two places, 17 stack kept for rollback

Doing this with CICDoo

On CICDoo, each branch of your repository runs as its own instance, so a porting branch such as 18.0 can run as a development or staging instance next to your 17 production instance, on your own servers. Non-production instances are neutralised when Odoo starts. Production and staging instances have backups you can create on demand, download and restore, which covers the pre-upgrade copy. CICDoo supports Odoo Community and Enterprise, 11.0 to the latest release, plus master. If you would rather have engineers plan and run the upgrade with you, see managed operations or talk to us.

For more on running Odoo yourself, see the self-hosted Odoo overview and how to self-host Odoo 18 in production.

Part of Self-hosted Odoo, done right

FAQ

Questions, answered

Can I upgrade Odoo Community from 17 to 18?

Yes, but not with Odoo's upgrade service, which is for Enterprise. Use the OCA's OpenUpgrade project for the 18.0 branch, check its module coverage list first, and port your custom modules yourself.

How long does an Odoo 17 to 18 upgrade take?

The migration run itself is often minutes to a few hours depending on database size. The project around it, porting modules, rehearsals and user testing, usually takes weeks. Time your rehearsal to know your real downtime.

Does the Odoo upgrade service migrate custom modules?

Not by default. It migrates standard modules and Studio customisations. Custom module code has to be ported to 18 by you or a partner, unless you have a separate paid arrangement with Odoo for custom code.

What replaces name_get in Odoo 18?

Compute the display_name field instead, by overriding _compute_display_name with the right @api.depends. name_get was deprecated in 17 and is gone from the ORM in 18. For searching by name, implement _search_display_name.

Do I need to rename tree views to list in Odoo 18?

Yes. Odoo 18 uses list as the view type and the list element. Run odoo-bin upgrade_code --from 17.0 to rewrite most occurrences automatically, then review the diff and test every view.

Can I skip Odoo 17 and go from 16 straight to 18?

The Enterprise upgrade service can upgrade across several versions in one request. OpenUpgrade goes one version at a time. Either way, your custom modules must be ported to the 18 API, so check the official upgrade notes for every version you skip.

Run Odoo like this, without doing it by hand

CICDoo turns every step in this guide into a push to Git, on servers you own. Talk to an engineer about your setup.

No per-seat fees. Your servers stay yours.