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):
- Click Duplicate next to the production database.
- Enter a new name (e.g.,
mycompany-staging). - Wait for the clone to complete.
- 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:
# 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)#
# Dump
python3 odoo-bin db --dump production_db /tmp/prod.zip
# Restore
python3 odoo-bin db --restore staging_db /tmp/prod.zipNeutralizing the Staging Database#
After cloning, apply these changes to avoid side effects:
1. Disable outgoing mail (critical):
-- 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:
[options]
email_from = staging-noreply@example.com3. Disable scheduled cron jobs:
UPDATE ir_cron SET active = false WHERE active = true;Or keep only the jobs you need active for testing.
4. Update system parameters:
-- 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:
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:
[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:
docker run -d -p 1025:1025 -p 8025:8025 mailhog/mailhogConfigure 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:
#!/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-stagingCommon 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

