Business Management

Migrate Off Odoo.sh: Step-by-Step Checklist

J
James Eriksson
··15 min read
Step-by-step guide to migrate off Odoo.sh. Export database, restore to self-hosted Odoo, validate data, handle custom modules. Includes troubleshooting and staging migration.
TL;DR
  • Export your Odoo.sh database and filestore via the console backup tool or SSH
  • Restore the dump to a self-hosted PostgreSQL server running matching Odoo version
  • Validate data integrity and module functionality before declaring success
  • The 30-day staging database expiry forces migration decisions; migrate staging separately if you need it
  • Cost savings are 96% for a 30-person team moving from Odoo.sh ($12,000+/month) to self-hosted ($200-500/month)

Migrating off Odoo.sh means exporting your database, filestore, and custom modules to a self-hosted server or managed platform. This checklist walks you through each step, from pre-migration planning through post-migration validation.

Why Leave Odoo.sh? And the Staging Environment Expiry Problem

Odoo.sh offers managed hosting, but it comes with real constraints that force decisions. The per-user pricing model means at 30 employees, you are paying $50+/user/month. That adds up quickly. Flexibility is limited too: custom modules must fit Odoo.sh's deployment model, and upgrades are gated by Odoo's release schedule.

The staging environment expiry policy is the hard blocker for most teams. Odoo.sh automatically deletes staging databases after 30 days of inactivity. If you have a staging environment for testing upgrades or new customizations, it vanishes. You cannot back it up or restore it. This means you must migrate off or constantly refresh your staging instances to keep them alive.

The math is simple. A 20-person Odoo team on Odoo.sh pays $12,000+/month. A managed alternative or self-hosted Odoo on a VPS costs $200-500/month. That is a 96% cost reduction. Downtime is the only real risk, and this guide minimizes it.

Odoo has 28M+ users globally. Many started on Odoo.sh and moved to self-hosted as they scaled. The migration is routine once you understand the steps.

Before You Start: Pre-Migration Planning Checklist

Migration failures happen because teams skip the planning phase. Your task here is to document your current setup, estimate scope, and plan downtime.

Document your current Odoo setup:

  • Record your Odoo version (e.g., Odoo 16, Odoo 19)
  • Count installed modules (navigate to Apps > List in Odoo; take a screenshot)
  • List custom modules (these live in /addons/ on Odoo.sh)
  • Record database size (in Odoo.sh console: manage databases > database > backup > file size)
  • Estimate filestore size (attachments, images, PDFs; rough total in MB or GB)
  • Screenshot your user count and active seat usage

Plan the migration window:

  • Choose a low-traffic time (e.g., Friday evening or Saturday)
  • Notify users 7 days in advance; announce 2-hour downtime
  • Brief your support team on the rollback plan (revert DNS if needed)
  • Ensure a backup of your production database exists AND is recent

Decide on upgrade strategy:

  • Same version migration (Odoo.sh 19 to self-hosted Odoo 19): simpler, less risk
  • Version upgrade (Odoo.sh 16 to self-hosted Odoo 19): requires database transformation
  • If upgrading, test OpenUpgrade on a copy first; it is free and open-source (github.com/OCA/OpenUpgrade)

Prepare your destination:

  • Provision a VPS or managed server (2+ vCPU, 4GB RAM minimum; scale up if your database is >20GB)
  • Choose your host: self-hosted on AWS/Hetzner/DigitalOcean, or a managed Odoo platform
  • Confirm database and file permissions; Odoo runs as odoo:odoo user

This phase takes 2-4 hours. Do not skip it.

Choose Your Migration Path

Three paths exist. Each has tradeoffs.

Path 1: Odoo.sh to Self-Hosted (Same Version)

  • Simplest and fastest
  • Export database + filestore from Odoo.sh, restore on self-hosted
  • Downtime: 30 minutes to 2 hours (depends on database size)
  • Cost: $200-500/month for a managed VPS; $0 if you self-manage
  • Risk: Low (no version upgrade complications)
  • Best for: Teams wanting to exit Odoo.sh cost immediately

Path 2: Odoo.sh to Self-Hosted + Version Upgrade

  • Export from Odoo.sh, transform database via OpenUpgrade, restore to target version
  • Downtime: 2-8 hours (depends on database size and module count)
  • Cost: $200-500/month for a managed VPS
  • Risk: Medium (database transformation can fail if custom modules are incompatible)
  • Best for: Teams wanting to modernize during migration
  • Tool: OCA OpenUpgrade (free, github.com/OCA/OpenUpgrade)

Path 3: Migrate to a Managed Alternative

  • No self-hosting required; another company manages Odoo for you
  • Examples: OEC.sh, Cloudpepper, Odoo Online (if you are willing to pay more)
  • Downtime: 1-4 hours (managed platforms often handle migration for you)
  • Cost: $200-1,500/month depending on feature tier
  • Risk: Low (managed platforms handle technical details)
  • Best for: Teams wanting infrastructure off their plate

Most teams choose Path 1 because it is fast and cheap. Path 2 adds complexity but future-proofs your Odoo version. Path 3 is for teams that want managed hosting without the Odoo.sh pricing model.

Step-by-Step: Export from Odoo.sh

Your database and files live on Odoo.sh servers. You need to extract them.

From the Odoo.sh Console:

  • Log into console.odoo.sh with your account
  • Select your production database from the list
  • Click "Backups" in the left menu
  • Click "Download" on the latest backup
  • A.zip file downloads containing: dump.sql (database), filestore/ folder (attachments)
  • Wait for the download to complete (may take 5-30 minutes for large databases)
  • Verify file size matches what you noted during planning (roughly equal to your database size)

Transfer the backup to your target server:

  • Use SCP, SSH, or S3 to copy the.zip to your new server
  • Command example: scp backup.zip user@your-server:/tmp/backup.zip
  • Verify the transfer completed: ls -lh /tmp/backup.zip should show the same file size

Extract the backup:

  • On your target server, extract: unzip /tmp/backup.zip -d /tmp/odoo-backup/
  • Confirm dump.sql and filestore/ are extracted: ls -la /tmp/odoo-backup/

If your database is larger than 5GB, Odoo.sh may time out the download. In that case, request a backup via SSH or contact Odoo.sh support for a manual export. For most teams, the console download works in one try.

Step-by-Step: Prepare Your Target Server

Before restoring, your destination must be ready.

Install PostgreSQL (if not already installed):

  • Check if installed: psql --version
  • If missing, install: sudo apt install postgresql postgresql-contrib (Ubuntu/Debian)
  • Start the service: sudo systemctl start postgresql && sudo systemctl enable postgresql

Install Odoo (matching your source version):

  • Download from odoo.com or GitHub (e.g., Odoo 19 from github.com/odoo/odoo)
  • Clone: git clone https://github.com/odoo/odoo.git --branch 19.0 /opt/odoo
  • Install dependencies: sudo pip install -r /opt/odoo/requirements.txt
  • Do NOT start Odoo yet; the database does not exist

Create an empty Odoo database user:

  • sudo -u postgres createuser odoo (PostgreSQL user)
  • sudo -u postgres psql: ALTER USER odoo WITH PASSWORD 'secure_password'; (set a password)
  • GRANT ALL PRIVILEGES ON DATABASE postgres TO odoo; (allow Odoo to create databases)

Prepare file paths:

  • Create the filestore directory: sudo mkdir -p /var/lib/odoo/filestore
  • Set ownership: sudo chown -R odoo:odoo /var/lib/odoo
  • Set permissions: sudo chmod -R 755 /var/lib/odoo

Test connectivity: sudo -u postgres psql -c "SELECT 1;" should return 1 without errors.

Step-by-Step: Restore Database and Filestore

Now you import your data into the new server.

Stop any running Odoo service:

  • sudo systemctl stop odoo (or whatever your Odoo service is named)
  • Confirm it stopped: ps aux | grep odoo (should see no odoo processes)

Restore the database:

  • Extract dump.sql: unzip /tmp/backup.zip dump.sql
  • Restore: sudo -u postgres pg_restore --no-owner /tmp/odoo-backup/dump.sql | psql -U odoo (adjust path to your dump.sql)
  • Verify: sudo -u postgres psql -l | grep odoo (should list your restored database)

Restore the filestore:

  • Copy the filestore folder: sudo cp -r /tmp/odoo-backup/filestore /var/lib/odoo/
  • Fix ownership: sudo chown -R odoo:odoo /var/lib/odoo/filestore
  • Fix permissions: sudo chmod -R 755 /var/lib/odoo/filestore

Start Odoo:

  • sudo systemctl start odoo
  • Tail the logs: sudo tail -f /var/log/odoo/odoo-server.log
  • Wait 2-3 minutes for Odoo to initialize the restored database
  • Look for: "odoo.service: Main process exited, code=exited" (error) vs. normal startup logs

Test login:

  • Open your Odoo URL in a browser
  • Log in as admin (same credentials as Odoo.sh)
  • Navigate to Settings > Users to confirm data is present

Common Migration Blockers and Troubleshooting

These errors happen. Here is how to fix them.

Error: "relation "xxx" does not exist"

  • Cause: Custom module tables are missing because the module code is not installed
  • Fix: Install custom modules first, then restart Odoo
  • Steps: Navigate to Apps > Update Apps List > Search for your custom module > Install
  • If the module does not show, copy it to /opt/odoo/addons/ manually and restart

Error: "XMLID xxx does not exist" or "View not found"

  • Cause: Broken view inheritance; a custom view depends on a base view that changed
  • Fix: Edit the custom view in Odoo UI (Settings > Technical > Views) and fix the inheritance chain
  • Or: In the database, UPDATE ir_ui_view SET active = FALSE WHERE xml_id LIKE 'your_module.%'; to disable broken views

Error: "Permission denied" when accessing filestore attachments

  • Cause: File permissions are wrong; Odoo cannot read the filestore
  • Fix: sudo chown -R odoo:odoo /var/lib/odoo/filestore && sudo chmod -R 755 /var/lib/odoo/filestore
  • Restart Odoo: sudo systemctl restart odoo

Error: "Database version mismatch" on module load

  • Cause: You migrated Odoo 16 database but installed Odoo 19
  • Fix: If intentional, use OpenUpgrade to transform the database first (see Path 2 in earlier section)
  • If accidental, restore the dump.sql to a server running matching Odoo version

Custom modules fail to load or have errors

  • Cause: Code incompatibility between Odoo versions or Python environment mismatch
  • Fix: Review the Odoo.sh console logs for the custom module; errors are often syntax-related
  • Common: Fields renamed (e.g., field_description -> field_help in newer versions)
  • Action: Update custom module code to match target Odoo version, re-install

Slow performance after migration

  • Cause: Database indices missing or VPS too small
  • Fix: Run ANALYZE on the database: sudo -u postgres vacuumdb --analyze your_database
  • Monitor resources: watch -n 1 'free -h; ps aux | grep odoo' (check CPU/memory)
  • Upgrade VPS if CPU/memory is maxed

Post-Migration Validation Checklist

Do not celebrate yet. Verify everything works.

Data Integrity (First 30 Minutes):

  • Log in as admin; verify no error banners
  • Navigate to Settings > Companies > Select your company > verify contact info, currency, tax config
  • Go to Contacts > search for a known customer > verify all fields are present (addresses, tax ID, etc.)
  • Open Accounting > Chart of Accounts > verify account list is complete (count should match Odoo.sh)
  • Check Inventory > Products > verify a few products load (image, price, stock level)

Module Functionality (30 Minutes to 1 Hour):

  • Apps > Installed Apps > Scroll through; no red error badges should be visible
  • Go to Sales > Quotations > Open a recent quote > verify line items, customer, total
  • Create a draft sales order (do not confirm) > Save and close > Re-open to confirm data persistence
  • Navigate to Invoicing > Invoices > Open a paid invoice > verify payment status, due date
  • Go to Projects (if using) > open a project > verify tasks and team members load

Critical Workflows (1-2 Hours):

  • Create a test sales order in draft > Confirm it > Generate a PDF (should include logo, customer data)
  • Download the PDF and verify formatting (numbers correct, images present)
  • Go to Invoicing > Invoices > Select an invoice > Print/Email dialog should open without errors
  • Run a report: Accounting > Reports > Trial Balance > Should populate and export to Excel without errors
  • Test scheduled actions: Go to Automation > Scheduled Actions > Select one > Click "Run Manually" > Should execute without errors in logs

Filestore and Attachments:

  • Go to a sales order or invoice > Click "Attachments" > If any exist, download one > File should open correctly
  • Go to Contacts > Select a contact with a profile picture > Image should load in the UI
  • Upload a test file to any record > File should save and be retrievable

Email and Integrations (1-2 Hours):

  • Go to Settings > Email > Outgoing Mail Servers > Verify credentials and click "Test Connection"
  • If you use payment gateways (Stripe, PayPal), test a payment flow in a test sales order
  • If you use API integrations, trigger one (e.g., sync with external CRM) > Logs should show success

Load and Stability (Continuous):

  • Monitor server logs: sudo tail -f /var/log/odoo/odoo-server.log
  • Watch for ERROR or CRITICAL messages for 30 minutes
  • Have a few users log in and use Odoo for 30 minutes
  • Check CPU and memory usage: top -u odoo (should be <80% CPU, <70% RAM for normal use)
  • If logs are quiet and users report no issues, migration is likely successful

Do not assume success because the UI loads. This checklist catches hidden data issues and permission bugs that break months later.

Migrating Your Staging Environment

Odoo.sh staging databases expire after 30 days of inactivity. Migrate staging separately if you need it long-term.

Why migrate staging: Your production environment is now self-hosted. If you keep staging on Odoo.sh, it expires. You lose the ability to test upgrades and custom modules. Solution: migrate staging to a second database on your self-hosted Odoo instance.

Staging migration steps:

  • In Odoo.sh console, select your staging database
  • Download its backup (Backups > Download) before the 30-day expiry
  • Transfer the backup to your self-hosted server (same SCP/SSH method as production)
  • Create a new empty database: sudo -u postgres createdb staging_odoo
  • Restore the staging dump: sudo -u postgres pg_restore /tmp/staging-backup/dump.sql | psql -U odoo -d staging_odoo
  • Copy filestore: sudo cp -r /tmp/staging-backup/filestore /var/lib/odoo/filestore-staging/
  • Configure Odoo to serve both databases: Edit /etc/odoo/odoo.conf > Set db_filter = staging_odoo to access staging via a subdomain or port
  • Restart Odoo: sudo systemctl restart odoo
  • Access staging: Navigate to your staging URL and verify data loads

Now both production and staging run on self-hosted Odoo with no expiry. Test freely.

Frequently Asked Questions

How do I export my Odoo.sh database if the download times out? For databases larger than 5-10GB, Odoo.sh downloads often timeout. Contact Odoo.sh support via your console and request a manual export. They can email you a backup link or SSH you access to download directly. Alternatively, use Odoo.sh SSH access: connect via SSH, navigate to /data/files/database.backup, and SCP it to your server.

What happens to my staging environment if I leave Odoo.sh? Your staging database is deleted automatically 30 days after your last login or activity. You cannot recover it. If you need staging long-term, migrate it to your self-hosted instance (see earlier section) or archive a backup before leaving Odoo.sh.

Can I migrate my custom modules off Odoo.sh? Yes, but custom module code must be updated if you are upgrading Odoo versions. Custom modules themselves are code files; they do not migrate with the database. Download them via SSH (scp -r user@odoo.sh:/path/to/addons your-server:/opt/odoo/addons/) and test each module on your target Odoo version. Modules often need fixes for syntax changes or API updates between versions.

How long does an Odoo.sh-to-self-hosted migration take? Typical timeline: 1-2 hours planning + 30 minutes to 4 hours migration + 1-2 hours validation. Most downtime occurs during database restore (depends on size: roughly 1 minute per 1GB). A 20GB database takes 20 minutes to restore. Filestore copy is usually fast. Total user-facing downtime: 1-3 hours for a smooth migration.

What is the 30-day staging database expiry policy? Odoo.sh automatically deletes staging databases if you do not access them for 30 days. This is a resource-conservation policy. If your staging database expires, you must restore it from a backup or migrate off Odoo.sh to self-hosted. The policy creates urgency: many teams decide to migrate when facing staging expiry.

Can I upgrade Odoo versions during platform migration? Yes, but it requires extra steps. Use OCA OpenUpgrade (free, github.com/OCA/OpenUpgrade) to transform your database from Odoo 16 to Odoo 19 before or after migrating servers. Test the upgrade on a copy first; if it fails, you still have your original. Upgrading adds 2-4 hours to the migration timeline because you are running two transformation steps instead of one.

How do I migrate the filestore and attachments? Filestore is a directory containing all document attachments, images, and reports. It is included in your Odoo.sh backup.zip file. Extract it and copy to /var/lib/odoo/filestore on your self-hosted server. Set ownership to odoo:odoo and permissions to 755. Test by downloading an attachment from a record in the migrated database.

What if the migration fails and I need to rollback? If your migrated production database is broken, revert: (1) Stop Odoo on the new server, (2) Point DNS back to Odoo.sh if you had already switched, (3) Tell users to refresh their browser and log in again via the old Odoo.sh URL. You lose any data entered after the migration started, so communicate downtime clearly. Rollback should take 5-15 minutes. Always test the migration on a copy of your database first to catch errors before going live.

The Bottom Line

Migrating off Odoo.sh is routine if you follow this checklist. Plan 2-4 hours of downtime, export your database and filestore, restore on a self-hosted VPS or managed platform, and validate your data. The cost reduction is immediate: a 30-person team saves $12,000+/month. The staging expiry policy forces the decision anyway, so do not delay.

Most migration failures stem from skipping validation or underestimating custom module rework. Allocate time to test workflows and check data. If you need hands-on help, Opsily's migration service handles export, restore, and validation for you. Learn more at our migration service page.

Hands-off Migration
Opsily handles the database export, restore, and validation so you stay focused on your business.
Get Started Free

Ready to self-host your own apps?

One server. Multiple apps. No per-app fees.

Get started →