Insights·Hosting·5 min read

Odoo hosting migration: what actually moves and what breaks

Copying the database is the easy part of moving an Odoo instance. The filestore, the addon versions, outbound mail, scheduled jobs and the connectors nobody documented are what turn a one-hour job into a bad week.

Moving Odoo from one host to another looks like a single task and turns out to be six. The database copy is usually the least of it. What takes the time is everything attached to that database: the filestore, the custom code, the outbound mail path, the scheduled jobs, and the integrations that were configured once and never written down.

This is a practical map of what moves cleanly, what does not, and how to plan a cutover that does not consume a working week.

What a migration is actually made of

An Odoo instance is four separate things, and each one moves differently:

  • The PostgreSQL database. Structured data, configuration, and the record of every transaction. It moves as a dump and a restore, and it is the most predictable part of the job.
  • The filestore. Attachments, product images, uploaded documents, generated PDFs. It lives on disk, not inside the database, and it is the single most commonly forgotten item in a migration.
  • Custom and third-party addons. The modules installed on the source server. If the target does not have the same modules at compatible versions, the restored database will not load at all.
  • The environment around it. DNS, SSL certificates, mail configuration, cron behavior, and whatever external systems still point at the old URL.

A migration plan that only accounts for the first item is the migration that goes wrong.

The filestore is where the surprises live

A database restore will succeed perfectly well without the filestore. That is the problem. Attachment records survive the restore and keep pointing at files that are no longer on disk, so nothing errors at cutover time. The failure surfaces weeks later, when someone opens a signed contract from March and gets an empty page, or a product image renders as a broken icon in the customer portal.

Copy the filestore alongside the dump, then verify it by size and file count rather than by eye. When you spot-check, open old attachments, not the ones uploaded yesterday. Recent files are the ones most likely to be present in any partial copy.

Addon parity, not just addon presence

Odoo builds its registry from the modules recorded as installed in the database. If a module is listed there and the code is missing on the target, or the code is present at a version whose fields no longer match, the instance fails to boot. This is not a graceful degradation. It is a database that will not come up.

Before you start, take an inventory of installed modules from the source instance and pin the custom ones to specific commits rather than to a moving branch. "We pull the latest" is how a migration turns into an unplanned upgrade project.

What breaks after a clean restore

  • Outbound email. The new host sends from a new IP address. If that IP is not covered by your SPF record, mail either lands in spam or is rejected outright, and the queue looks healthy the whole time.
  • Incoming mail. Fetchmail credentials and the catchall alias domain need to be re-pointed, or inbound replies stop attaching to their records.
  • Scheduled actions. A restored database carries its full cron schedule. If the old instance is still running, both instances fire the same jobs: duplicate invoices, double syncs, two sets of reminder emails. Freeze or disable crons on the source before you open the target to users.
  • Third-party connectors. Payment gateways, shipping providers, messaging integrations and e-invoicing endpoints all hold callback URLs and credentials that are environment-specific. Where compliance is involved, note that ZATCA, ETA, FTA and WPS support comes from Odoo's official localization modules, and those modules carry their own environment configuration that has to be re-entered rather than assumed.
  • The base URL. Portal links, password reset emails and shared invoice links are all built from the system base URL parameter. Miss it and your customers receive working emails full of dead links.

Plan the cutover, not just the copy

The single highest-value step is a rehearsal. Do a full restore onto the target days before the real move, and keep a written list of every fix you had to apply. That list is your cutover runbook, and it is the only reliable estimate of how long the real window needs to be.

On the day itself, the order matters: lock users out of the source, take the final dump and filestore copy, restore, apply the runbook, re-point DNS, and only then let people back in. Lower your DNS TTL a day in advance so the switch propagates in minutes instead of hours.

Then smoke test as a normal user, not as an administrator. Log in with a standard account, open an old invoice PDF, post a journal entry, trigger one outbound email, run one integration end to end, and confirm that a scheduled job has fired since the cutover. Keep the old instance intact but frozen for a week or two afterward. Deleting it the same evening removes your only rollback path.

What managed hosting changes

A managed platform removes some of these steps and none of the decisions. Plemo, our managed Odoo hosting, covers the backup schedule, point-in-time recovery and the staging environment, which is what makes a rehearsal restore a routine operation rather than a small project of its own. Re-pointing connectors, updating the SPF record and freezing the source crons are still deliberate choices someone has to make and verify.

If the instance you are moving is business-critical and you would rather not own the runbook yourself, that is what an ongoing support arrangement is for: see managed support and SLA for how that engagement is structured.

Let's talk

Let's turn this into a plan.

Book a discovery call and we will map your fastest path to a system that lasts.

Book a discovery call