Odoo Data Migration: How to Get It Right the First Time

Of every line in an ERP project plan, data migration is the one I see underestimated most often. After 100-plus Odoo implementations, I can tell you the software rarely sinks a go-live — the data does. It arrives late, it arrives dirty, and it gets loaded once, in a rush, with no clean way to undo it. Here is how I keep that from happening: what "migration" really means, what to move, and the mechanics that prevent a mess.

Two different problems people call migration

When a client says "migration," they usually mean one of two very different jobs, and conflating them is the first mistake.

The first is a fresh-system data import: moving off a legacy system — spreadsheets, an old accounting package, another ERP — into a brand-new Odoo database. Odoo imports into any of its business objects — contacts, products, bank statements, journal entries, orders and the rest — from either Excel (.xlsx) or CSV (.csv) files.

The second is a version upgrade: your data is already in Odoo, and you are moving the database from one major version to a newer one. That is not an import — it is a managed process Odoo runs on its own upgrade platform. Odoo draws a hard line here: the upgrade service does not cover migrating from another ERP into Odoo, or switching editions or hosting type. The tools and risks differ completely, so I'll take them in turn.

What to migrate and what to leave behind

Before touching a single import file, decide what actually needs to move. This is the conversation that saves the most money, and my default answer surprises people: move less than you think.

  • Master data — yes. Customers, vendors, products, the chart of accounts. Odoo has a dedicated business object for each.
  • Open items — yes. Unpaid invoices, open supplier bills, undelivered orders, current stock. You need these live on day one to keep trading.
  • Opening balances — yes, but as balances, not history. Load your closing trial-balance position as opening journal entries so the books are correct from go-live.
  • Full transaction history — usually no. Years of posted invoices and closed tickets are the biggest source of migration effort and the least-opened data afterward.

My honest opinion after a hundred projects: dragging a decade of closed transactions into a fresh database is rarely worth it. Keep the old system read-only as your archive for the odd old invoice. It is a judgement call, not a rule, but the default should be lean.

The import mechanics that save you

Map your columns deliberately. When you upload a file, Odoo heuristically works out each column's field type from the first ten lines — a column of pure numbers, for instance, is only offered integer-type fields. Any column it misses, you set by hand from a dropdown, and in developer mode an "Allow matching with subfields" option gives you finer control.

Always click Test first. The workflow is: upload the file, adjust the CSV formatting options if needed, map each column to an Odoo field, click Test to confirm the data is valid, and only then Import. Odoo's documentation is blunt that imports are permanent and cannot be undone; the created-on and last-modified filters only help you find what a bad import touched afterward.

Use External IDs. This separates a controlled migration from a duplicate-ridden mess. An External ID is a unique identifier for each row, and you can reuse the IDs from your old software. Their power is in re-runs: if your file includes an External ID (or Database ID) column, Odoo modifies the records already imported instead of creating new ones. That is what lets you import the same file several times without duplicates — and the first load is never perfect. The catch is consistency: if an External ID is altered or removed between runs, Odoo may add a duplicate instead of updating the original. So keep them stable and never delete the ID column.

Odoo even builds a round-trip for updates: tick "I want to update data (import-compatible export)" in the Export pop-up and it adds the External ID column automatically, so you can edit the file and re-import it as a controlled bulk update. And plan for scale: very large imports can time out, so work in smaller batches.

Version upgrades: the official path

Upgrading an existing Odoo database is a different discipline that Odoo runs as a managed service. The shape is always the same: request an upgraded test database on your target version, test it hard, then — and only then — request the production upgrade.

The test database is your safety net, and Odoo neutralises it so it cannot reach the real world: scheduled actions are disabled, outgoing mail servers are switched off, payment providers and delivery carriers drop to sandbox mode, and bank synchronisation is turned off.

The production run mirrors the test, with the purpose set to Production — and it is a one-way door. Once requested, the database is unavailable until the upgrade finishes, and once it completes you cannot revert. So I schedule it for a low-usage window and rehearse on a fresh test database first.

Two constraints catch people out. Versions: you can only upgrade to a currently supported release, each major version getting three years of standard support, with the last unsupported version usable as a target for six months past its end-of-life. Custom code: a database with custom modules cannot be upgraded until a version of those modules exists for the target release. Odoo's service covers standard apps and Studio customisations but excludes modules built in-house or by third parties — Odoo partners included — unless they sit under a maintenance-of-customisations contract. The upgrade is free on Odoo Enterprise; making your customisations compatible is the part you plan and resource.

The through-line: whether you are importing into a new system or upgrading an old one, the rule is the same — never do it once, never do it in production first, and always keep a way to check the result before anyone starts working.

The migration mistakes I keep seeing

The same failures recur, and every one is avoidable:

  • Loading dirty master data as-is. Duplicated customers and half-finished products do not improve inside Odoo — they multiply. Clean before you load.
  • Skipping External IDs. Without them, your second import run duplicates records instead of updating them — and the first run is never perfect.
  • Migrating history nobody reads. Teams burn weeks moving years of closed transactions no one opens again. Archive the old system instead.
  • No reconciliation sign-off. If finance has not confirmed that opening balances and open items tie back to the old system, you are not live — you are guessing.
  • Big-bang with no test cycles. One import or upgrade, straight into production, no rehearsal. Imports cannot be undone and production upgrades cannot be reverted — the riskiest way to run the job.

How I run a migration

My sequence rarely changes:

  1. Scope the data — agree what moves and what stays in the archive.
  2. Clean at source — deduplicate and fix the data before it goes near Odoo.
  3. Build the templates — one per object, with an External ID on every record.
  4. Test-import into a test database, in batches, using the Test step every time.
  5. Reconcile and get written sign-off from finance on balances and open items.
  6. Cut over — final load into production during a quiet window, then verify counts and totals.

For a version upgrade the spine is identical: test database, rehearse, reconcile, then a scheduled production run. That discipline is what makes migration boring — exactly what you want on go-live day.

Odoo's import tools, upgrade process and version-support windows change over time, and the details here reflect Odoo's published documentation for version 19.0 as of July 2026. Always confirm the current behaviour in the Odoo import documentation and upgrade documentation before planning a project around them.

Frequently asked questions

Can I undo a data import in Odoo?
No. Odoo's documentation is explicit that imports are permanent and cannot be undone — there is no rollback button. That is exactly why I never load straight into production without running the built-in Test step first, which validates the file before anything is written. If an import does go wrong, the created-on and last-modified filters can help you find the records it touched, but that is cleanup, not an undo. Work in small batches and keep a backup before every run.
How do I import data into Odoo without creating duplicates?
Use External IDs. Odoo treats the External ID, or Database ID, as a unique identifier for each row, and if your import file contains that column, records that have already been imported are modified instead of being created — Odoo decides automatically, row by row, whether to create or update. This is what lets you import the same file several times without creating duplicates. The catch is consistency: if an External ID is altered or removed between runs, the system may add a duplicate instead of updating the existing record, so never delete the ID column.
How does an Odoo version upgrade work?
Odoo runs it as a managed service with a mandatory test-then-production flow. You first request an upgraded copy of your database on the target version and test it thoroughly — these test databases are neutralised, so scheduled actions, outgoing mail and bank synchronisation are switched off and cannot affect the real world — and only once you are confident do you request the production upgrade. During the production run the database is unavailable until the upgrade finishes, and once it completes it is impossible to revert to the previous version, so I always rehearse on a fresh test database first.
Will my custom modules survive an Odoo upgrade?
Not automatically. Odoo's documentation states that a database containing custom modules cannot be upgraded until a version of those modules is available for the target release, so the code has to be made compatible first. Odoo's upgrade service covers standard applications and Studio customisations, but it explicitly excludes modules built in-house or by third parties — including Odoo partners — unless they are under a maintenance-of-customisations subscription. Plan the custom-code upgrade as its own workstream, running in parallel with the database upgrade.

Related reading

More guides from my desk: what actually drives Odoo implementation cost in the UAE — where data migration is one of the biggest and most underestimated drivers; Odoo 19 new features — what actually matters — a first-hand look at the release you may be upgrading toward; and Odoo Community vs Enterprise — how to choose — the edition decision that determines whether your upgrades are free and managed.

Planning a migration or an upgrade?

I'm an Odoo Techno-Functional Consultant in Dubai and Head of Projects at Techbot Information Technology LLC, a UAE Odoo partner — 10+ years, 100+ implementations, certified across Odoo v13, v14, v15, v16, v18 and v19, and I presented the new features of Odoo 19 at Odoo Experience 2025 in Brussels. Tell me about your current system and I'll give you a scoped, honest view of what your migration or upgrade would actually involve — at no cost.

Get a free business evaluation