Why You Need a Staging Environment
Every Odoo update — whether a module upgrade, a configuration change, or a custom development — should be tested on a copy of production before touching the live database. A staging environment is not optional for business-critical installations; it is what separates teams that have Odoo outages from teams that do not.
Architecture: Separate Docker Stack
The simplest approach is a second Docker Compose stack on the same server (or a separate staging server), pointing to a cloned database:
Production: ports 8069/8072 -> db: mydb_prod
Staging: ports 8070/8073 -> db: mydb_staging
Both stacks can share the same PostgreSQL instance if your server has enough RAM.
Step 1 — Clone the Production Database
# Option A: pg_dump / pg_restore (safest, works across versions)
pg_dump -U odoo -Fc mydb_prod > /tmp/prod_backup.dump
createdb -U odoo mydb_staging
pg_restore -U odoo -d mydb_staging /tmp/prod_backup.dump
# Option B: PostgreSQL createdb with template (fast, same server only)
createdb -U odoo -T mydb_prod mydb_staging
# Option C: Odoo database manager (UI)
# Database Manager > Duplicate > enter new name
Step 2 — Create the Staging Docker Compose File
# docker-compose.staging.yml
version: "3.8"
services:
odoo_staging:
image: odoo:19
ports:
- "127.0.0.1:8070:8069"
- "127.0.0.1:8073:8072"
volumes:
- ./staging_data:/var/lib/odoo
- ./addons:/mnt/extra-addons
- ./staging.conf:/etc/odoo/odoo.conf
depends_on:
- db
db:
image: postgres:16
# (or reuse your existing db container if on same network)
# staging.conf
[options]
db_name = mydb_staging
db_host = db
workers = 2
list_db = False
Step 3 — Configure a Staging Subdomain
# nginx: staging.example.com -> 127.0.0.1:8070
server {
listen 443 ssl;
server_name staging.odoo.example.com;
location / {
proxy_pass http://127.0.0.1:8070;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Get a free SSL certificate with Certbot: certbot --nginx -d staging.odoo.example.com
Step 4 — Anonymise Sensitive Data
Production data should never be accessible to developers who do not have production access. Anonymise it after cloning:
# Connect to Odoo shell on staging:
python odoo-bin shell -d mydb_staging --config staging.conf
# Anonymise partner data:
partners = env['res.partner'].search([('email', '!=', False)])
for p in partners:
p.write({
'email': f'user_{p.id}@staging.example.com',
'phone': '555-0000',
'mobile': False,
})
env.cr.commit()
exit()
Also update mail server settings to prevent staging from sending real emails:
# In staging.conf:
smtp_server = localhost # point to a fake SMTP like MailHog
Step 5 — Update System Parameters for Staging
After cloning production, these settings must be updated for staging:
-- In psql connected to mydb_staging:
UPDATE ir_config_parameter SET value = 'https://staging.odoo.example.com' WHERE key = 'web.base.url';
UPDATE ir_config_parameter SET value = '' WHERE key = 'web.base.url.freeze';
-- Disable any payment providers:
UPDATE payment_provider SET state = 'disabled' WHERE state = 'enabled';
Step 6 — Regular Sync Workflow
Staging drifts from production over time. Establish a sync schedule:
#!/bin/bash
# sync-staging.sh — run weekly or before major testing
set -e
# Stop staging Odoo (keep DB accessible)
docker compose -f docker-compose.staging.yml stop odoo_staging
# Drop and recreate staging DB
psql -U odoo -c "DROP DATABASE IF EXISTS mydb_staging;"
pg_dump -U odoo -Fc mydb_prod | pg_restore -U odoo -d mydb_staging --create
# Re-run anonymisation and parameter updates (script them)
./anonymise-staging.sh
# Start staging Odoo
docker compose -f docker-compose.staging.yml start odoo_staging
echo "Staging synced from production at $(date)"
How DeployMonkey Handles Staging
DeployMonkey's control panel includes a one-click "Clone to Staging" feature. It duplicates your production instance (including the database and filestore), anonymises partner data, disables payment providers, and configures a separate subdomain — all automatically. No shell access required. See Backup Odoo Database for the underlying backup workflow.
Start free at deploymonkey.app.
Frequently Asked Questions
Can I use staging as a demo environment for new users?
Yes — but refresh it from production first and ensure all sensitive data is anonymised. Keep access restricted to internal users or use HTTP basic auth at the nginx level.
How often should I sync staging from production?
Before any significant testing session. Weekly is a good default — monthly is too infrequent to catch issues that arise from production data patterns.
Does staging need the same amount of server resources as production?
Not necessarily — staging usually has far fewer concurrent users. 2 workers and 4 GB RAM is sufficient for most staging environments.
How do I prevent staging from accidentally emailing customers?
Set smtp_server in staging.conf to a fake SMTP (MailHog, Mailpit) or a catch-all address. Also disable or override all outgoing mail server records in the staging database.