All posts
Integrator9 min read

Cloning an Odoo Database for Staging and Testing

Clone your Odoo production database to a staging or testing environment - use the Odoo manager duplicate feature, pg_dump/restore, or automated scripts; then neutralize outgoing mail, disable cron jobs, and set the correct system parameters.

Cloning an Odoo Database for Staging and Testing#

Why Clone to Staging?#

A staging clone lets you:

  • Test module upgrades before applying them to production
  • Train users on real data without risking the live system
  • Reproduce and debug production bugs in a safe environment
  • Test integrations with realistic data volumes

Method 1: Odoo Database Manager (Simplest)#

Settings → Manage Databases (or /web/database/manager):

  1. Click Duplicate next to the production database.
  2. Enter a new name (e.g., mycompany-staging).
  3. Wait for the clone to complete.
  4. Log into the new database and neutralize it (see below).

This copies both the PostgreSQL database and the filestore (/var/lib/odoo/filestore/).

Limitation: Only works if the database manager is enabled and accessible. Disable it on production after use.

Method 2: pg_dump + pg_restore#

For large databases or when the database manager is unavailable:

bash
# 1. Dump the production database
pg_dump -Fc -U odoo -h localhost production_db > /tmp/prod_backup.dump

# 2. Create the target database
createdb -U odoo staging_db

# 3. Restore
pg_restore -U odoo -d staging_db /tmp/prod_backup.dump

# 4. Copy the filestore
rsync -a /var/lib/odoo/filestore/production_db/ /var/lib/odoo/filestore/staging_db/

Method 3: Odoo CLI (Community Edition)#

bash
# Dump
python3 odoo-bin db --dump production_db /tmp/prod.zip

# Restore
python3 odoo-bin db --restore staging_db /tmp/prod.zip

Neutralizing the Staging Database#

After cloning, apply these changes to avoid side effects:

1. Disable outgoing mail (critical):

sql
-- Disable all outgoing mail servers
UPDATE ir_mail_server SET active = false;

-- Or redirect all outgoing mail to a catch-all test address
UPDATE ir_mail_server SET smtp_user = 'staging-test@example.com';

Or in Odoo: Settings → Technical → Outgoing Mail Servers → disable all.

2. Set catch-all for outgoing mail:

In odoo.conf for staging:

ini
[options]
email_from = staging-noreply@example.com

3. Disable scheduled cron jobs:

sql
UPDATE ir_cron SET active = false WHERE active = true;

Or keep only the jobs you need active for testing.

4. Update system parameters:

sql
-- Update the base URL
UPDATE ir_config_parameter
SET value = 'https://staging.mycompany.com'
WHERE key = 'web.base.url';

-- Clear any production API tokens
UPDATE ir_config_parameter
SET value = ''
WHERE key IN (
    'payment_stripe.secret_key',
    'payment_paypal.client_id'
);

5. Disable Stripe/payment providers:

Settings → Payment Providers → disable or set to Test mode.

6. Remove or expire user sessions:

sql
DELETE FROM ir_sessions;

Forces all users to log in again on the staging instance.

Staging Configuration in odoo.conf#

Run staging with a separate config file:

ini
[options]
db_name = staging_db
http_port = 8070
longpolling_port = 8072
logfile = /var/log/odoo/staging.log
email_from = False          ; Prevents sending any real emails
smtp_server = localhost      ; Redirect to a local mail catcher (e.g., Mailhog)

Using Mailhog for Email Testing#

Run Mailhog to catch all outgoing emails in staging:

bash
docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhog

Configure Odoo staging to send mail to localhost:1025. All emails appear in Mailhog's web UI at port 8025 instead of reaching real recipients.

Automating Staging Refresh#

Schedule a weekly database refresh script:

bash
#!/bin/bash
# refresh_staging.sh
set -e

# Stop staging Odoo
systemctl stop odoo-staging

# Drop old staging DB
dropdb --if-exists staging_db

# Clone production
createdb staging_db
pg_dump -Fc production_db | pg_restore -d staging_db

# Sync filestore
rsync -a --delete /var/lib/odoo/filestore/production_db/ /var/lib/odoo/filestore/staging_db/

# Neutralize
psql staging_db -c "UPDATE ir_mail_server SET active = false;"
psql staging_db -c "UPDATE ir_cron SET active = false;"
psql staging_db -c "UPDATE ir_config_parameter SET value = 'https://staging.mycompany.com' WHERE key = 'web.base.url';"

# Restart staging
systemctl start odoo-staging

Common Mistakes#

  • Forgetting to disable outgoing mail - a staging clone will send real emails to real customers unless outgoing mail is disabled immediately after cloning
  • Not updating web.base.url - links generated by Odoo (e.g., reset password emails) point to production if the base URL is not updated
  • Using the same filestore location - cloning without copying the filestore results in missing attachments; always copy the filestore directory
  • Running production crons on staging - scheduled cron jobs (e.g., invoice reminders, automatic payments) can affect real data if they call external APIs with production credentials
  • Not anonymizing sensitive data - for staging environments accessible to external parties, consider running a data anonymization script on PII fields

Try ERPeek on your own Odoo module - ask questions, scaffold tests, and explore your codebase in plain language.

Get started free