Introduction
Migrating an Odoo Community Edition database across major versions is one of the most challenging undertakings in the Odoo ecosystem. Each major release can introduce breaking schema changes, renamed fields, removed modules, and new ORM behaviours. Performing a direct leap — say, from version 13.0 to 18.0 — is not feasible; you must walk through every intermediate version sequentially.
The Odoo OpenUpgrade Wizard (CLI alias: oow) is an open-source tool
that streamlines this process. It wraps the OCA OpenUpgrade framework and
automates the repetitive, error-prone plumbing: fetching the correct source code
for each version, building Docker-based migration environments, managing
PostgreSQL databases, and executing the actual upgrade scripts — all from a
single, declarative YAML configuration.
Key benefits at a glance:
- Reproducible, Docker-based migration environments per Odoo version.
- Declarative
config.ymlthat describes the entire migration path.- Built-in database backup/restore, shell access, and workload estimation.
- Support for custom SQL and Python hooks before/after each step.
This guide covers the three major phases of an oow-driven migration and the
iterative testing workflow that makes the process manageable.
Phase 1 — Building the Migration Codebase
Before any database can be migrated you need the right source code and Docker images for every version in the migration path. This is a one-time setup (with occasional refreshes when upstream changes land).
1.1 Create a virtual environment and install oow
# Create and activate a Python virtual environment
python3 -m venv .venv
source .venv/bin/activate
# Install the wizard
pip install odoo-openupgrade-wizard
# — or, if you prefer an isolated install —
pipx install odoo-openupgrade-wizard
Tip: Make sure the version of git-aggregator installed in the same environment is ≥ 4.1. Older versions (e.g. 2.1) may silently produce incorrect aggregated repositories.
1.2 Initialise the project
oow init \
--initial-version=13.0 \
--final-version=18.0 \
--project-name=my-migration
This scaffolds the project directory with a config.yml and a src/ tree
containing one env_<version> folder per Odoo version in the migration path:
my-migration/
├── config.yml
└── src/
├── env_13.0/
├── env_14.0/
├── env_15.0/
├── env_16.0/
├── env_17.0/
└── env_18.0/
The config.yml is the heart of the project. It describes:
- PostgreSQL settings — image, container name, volume, performance tuning.
- Odoo versions — the list of versions in the migration path.
- Migration steps — each step maps to a version and an execution context
(
openupgradeorregular).
Each step executes three actions in order:
- Run
pre-migration.sqlscripts (custom SQL hooks). - Apply
--update=allusing either the standard Odoo codebase (regular) or the OCA OpenUpgrade fork (openupgrade). - Run
post-migration.pyscripts via Odoo shell (custom Python hooks).
The two execution contexts differ in which Odoo codebase performs the update:
openupgrade— uses the OCA OpenUpgrade fork, which includes migration scripts that handle schema changes, field renames, data transformations, etc. between major versions. This is the context used for cross-version leaps.regular— uses the standard Odoo CE codebase and runs--update=all. Typically used at the start and end of the migration path to ensure all modules are cleanly loaded on the source and target versions.
Tip: In
config.yml, theregularsteps have anupdate: Truesetting by default. You can set it toupdate: Falseif you want to skip the time-consuming--update=alland only execute the SQL/Python hook scripts.
A typical migration path from 13.0 to 18.0 would define 7 steps:
| Step | Version | Context | Purpose |
|---|---|---|---|
| 1 | 13.0 | regular |
Run Odoo --update=all on source version (pre-flight check) |
| 2 | 14.0 | openupgrade |
Migrate database from 13.0 → 14.0 via OpenUpgrade |
| 3 | 15.0 | openupgrade |
Migrate database from 14.0 → 15.0 via OpenUpgrade |
| 4 | 16.0 | openupgrade |
Migrate database from 15.0 → 16.0 via OpenUpgrade |
| 5 | 17.0 | openupgrade |
Migrate database from 16.0 → 17.0 via OpenUpgrade |
| 6 | 18.0 | openupgrade |
Migrate database from 17.0 → 18.0 via OpenUpgrade |
| 7 | 18.0 | regular |
Run Odoo --update=all on target version (finalise modules) |
1.3 Configure repos.yml per version
Inside each src/env_<version>/ directory, edit the repos.yml file to declare
the Git repositories that git-aggregator should fetch. This is where you add
your own custom module repositories alongside the standard Odoo and OpenUpgrade
sources.
# src/env_14.0/repos.yml (example)
./src/openupgrade:
remotes:
oca: https://github.com/OCA/OpenUpgrade.git
merges:
- oca 14.0
target: oca 14.0
./src/custom-addons:
remotes:
origin: git@gitlab.example.com:my-org/custom-addons.git
merges:
- origin 14.0
target: origin 14.0
1.4 Fetch the source code
oow get-code # Fetch all versions
oow get-code -v 14.0 # Fetch a single version
Under the hood oow invokes gitaggregate for each version, pulling Odoo CE,
OpenUpgrade, and any custom repositories declared in repos.yml.
1.5 Build the Docker images
oow docker-build
This builds one Docker image per Odoo version. Each image bundles the correct
Python runtime, system dependencies, and the source code fetched in the previous
step. The images are used by every subsequent oow command that needs to run
Odoo.
Phase 2 — Restoring the Source Database
With the images ready, the next step is to load a production (or staging)
database dump into the oow-managed PostgreSQL container.
oow restoredb \
--database mydb \
--database-path ./production.pgdump \
--filestore-path ./filestore \
--filestore-format d
| Flag | Description |
|---|---|
--database |
Name for the restored database inside the container. |
--database-path |
Path to the .pgdump / .sql / .dump file. |
--filestore-path |
Path to the Odoo filestore directory. |
--filestore-format |
d for a directory-style filestore, t for a tarball. |
The filestore contains attachments, report templates, and other binary assets managed by Odoo outside of PostgreSQL. Do not skip it — a migration without the filestore will result in missing images, documents, and reports.
Phase 3 — Running the Migration
Once the database is restored, kick off the sequential upgrade:
oow upgrade \
--database mydb \
--first-step 2 \
--last-step 7
This iterates through each migration step defined in config.yml, running the
OpenUpgrade (or regular Odoo) update process for every version in order. Each
step starts a Docker container with the matching Odoo image, connects to the
database, and executes the upgrade.
You can also run a single step at a time for tighter control:
# Migrate only from 13.0 → 14.0 (step 2)
oow upgrade --database mydb --first-step 2 --last-step 2
The Iterative Testing Loop
A multi-version migration is rarely a one-shot success. In practice, you will cycle through Phase 2 and Phase 3 many times: restore a fresh copy of the database, attempt the migration, inspect the outcome, fix issues in custom modules or pre/post-migration scripts, and try again.
The diagram below illustrates this loop and the supporting oow commands that
assist at each stage.
┌─────────┐
│ Start │
└────┬────┘
│
┌─────────┴─────────┐
│ Phase 2: Restore │
│ (oow restoredb) │
└─────────┬─────────┘
│
▼
┌─────────────────────────────┐
│ Phase 3: Run Migration Step │◄────────────────────────────┐
│ (oow upgrade --step N) │ │
└──────────────┬──────────────┘ │
│ │
▼ │
┌───────────────────┐ │
│ Inspect Logs/Data │ │
└─────────┬─────────┘ │
│ │
________▼________ │
┌──── / \ │
│ < Errors Found? > │
│ \_________________/ │
│ │ │
│ Yes │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Diagnose Issue │ │
│ │ (oow run, psql) │ │
│ └─────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
No │ Fix code/scripts │ │
│ └─────────┬─────────┘ │
│ │ │
│ ________▼________ │
│ / \ │
│ < Clean DB Need? > ─── No ───────────────────────────┤
│ \_________________/ │
│ │ │
│ Yes │
│ │ │
│ ▼ │
│ ┌───────────────────┐ │
│ │ Drop/Restore DB │ │
│ └─────────┬─────────┘ │
│ │ │
│ └────────────────────────────────────────────┤
│ │
▼ │
┌─────────────────────┐ │
│ Backup Database │ │
│ (oow dumpdb) │ │
└──────────┬──────────┘ │
│ │
________▼________ │
/ \ │
< More steps rem? > ─── Yes ─────────────────────────────────┘
\_________________/
│
No
│
▼
┌─────────────────────┐
│ Migration Complete! │
└─────────────────────┘
Key commands during the loop
| Command | Purpose |
|---|---|
oow restoredb |
Drop the current database and restore a fresh copy from the dump file. |
oow upgrade |
Execute one or more migration steps. |
oow run |
Start an Odoo instance against the database so you can browse the UI, verify data, and test business flows interactively. |
oow dumpdb |
Create a backup (.dump + filestore) of the database at its current state. Use this after a successful step so you don’t have to re-run earlier steps if a later step fails. |
oow psql |
Open a psql shell connected to the migration PostgreSQL container. Invaluable for running ad-hoc queries, diagnosing long-running operations, and analyzing schema changes. |
Putting It All Together — A Cheat Sheet
# ---- Phase 1: Setup (once) ----
python3 -m venv .venv && source .venv/bin/activate
pip install odoo-openupgrade-wizard
oow init --initial-version=13.0 --final-version=18.0 --project-name=my-mig
# Edit repos.yml in each src/env_*/ directory
oow get-code
oow docker-build
# ---- Phase 2: Restore ----
oow restoredb --database mydb \
--database-path ./production.pgdump \
--filestore-path ./filestore \
--filestore-format d
# ---- Phase 3: Migrate (step by step) ----
oow upgrade --database mydb --first-step 2 --last-step 2 # 13 → 14
oow dumpdb --database mydb --database-path .out/mydb-s2.dump
oow upgrade --database mydb --first-step 3 --last-step 3 # 14 → 15
oow dumpdb --database mydb --database-path .out/mydb-s3.dump
# ... repeat for each step ...
oow upgrade --database mydb --first-step 7 --last-step 7 # final update on 18
# ---- Debug helpers (anytime) ----
oow run --database mydb --step 6 # launch Odoo 18 UI
oow psql --database mydb --command "SELECT count(*) FROM res_partner;"
Tips and Gotchas
-
Take dumps after every successful step. Storage is cheap; re-running a six-version migration from scratch because step 5 failed is not.
-
Watch for
404 Client Error for http+docker— this is a transient Docker networking issue. Simply re-run the command. -
Validate data between steps. Use
oow runto launch the Odoo UI and spot-check critical business flows (invoices, stock moves, accounting entries). Useoow psqlto run sanity-check SQL queries. -
Custom pre/post migration scripts can be placed alongside the migration step configuration. Use these to clean up orphaned data, rename deprecated fields, or install/uninstall modules before the upgrade runs.
-
Parallel restore — if your dump is large, configure
postgres_restore_options.jobsinconfig.ymlto speed upoow restoredb:postgres_restore_options: jobs: 4
Conclusion
The Odoo OpenUpgrade Wizard transforms what would otherwise be a brittle, manual, multi-day process into a reproducible, scriptable workflow. By separating concerns into three clear phases — codebase setup, database restore, and iterative upgrade — it gives you confidence that each migration step is isolated, testable, and recoverable.
The real power, however, lies in the loop: restore, upgrade, inspect, fix,
repeat. With oow dumpdb as your safety net and oow run / oow psql as your
diagnostic tools, you can methodically work through even the most complex
migrations without losing progress.
Happy migrating! 🚀
Further Reading & Resources
For a deeper dive into the philosophy and advanced usage of the Odoo OpenUpgrade Wizard, we highly recommend the following presentations from the Odoo Community Association (OCA) events:
📺 Open Upgrade Roundtable
OCA Days 2025 (Holger Brunn, Hugues de Keyser, Sergio Corato) The most recent discussion among the core maintainers and users regarding the current state and future of Odoo migrations and the tooling surrounding it.
📺 Mastering Version Upgrades with OpenUpgrade
OCA Days 2024 (Miquel Raïch) A detailed workshop focusing on advanced migration strategies and mastering the upgrade process sequentially.
📺 Introducing odoo-openupgrade-wizard
OCA Days 2023 (Rémy Taymans) The original introduction to the tool by its author, explaining the core workflow and the “magic” of automated Odoo migrations.