Development, staging and production for Odoo: a practical workflow
How to run Odoo releases through development, staging and production: branches as environments, tests on every push, rehearsals on a neutralized copy of production, version bumps, rollback and upgrades.
By Odoo Cloud · Published · 7 min read · Workflow, Staging, Odoo development
Most Odoo incidents are not caused by bad code. They are caused by good code meeting data it has never seen: a module that works on demo data and fails on ten thousand real invoices, a view that breaks on a field someone customised, a migration script that takes forty minutes instead of four. The cure is a workflow where every change is tried on real data before it reaches production.
This article describes that workflow as it runs on Odoo Cloud, but the ideas apply to any Odoo setup.
Three stages, three kinds of database
Every branch of your repository is an environment with its own URL, database, logs and shell. What changes between stages is the database each one starts from.
| Stage | Database | Purpose |
|---|---|---|
| Development | A fresh database with demo data on every build | Write code, run tests |
| Staging | A neutralized copy of production | Rehearse the release on real data |
| Production | Your live database, kept across releases | Serve your users |
The rule of thumb: development proves the code works, staging proves it works on your data, and production only ever receives what staging has already seen.
Development: one branch per change
Create a branch for each feature or fix and push it. A new branch appears as a development environment, and each push builds it again:
- The database is fresh, with Odoo's demo data, so every build starts from a known state.
- Your modules are installed and their tests run. A build with failing tests is marked red but stays up, so you can connect and look at what went wrong.
- Build status is reported on your GitHub commits, so reviewers see it in the pull request.
Each development branch has a Settings tab where you choose what a new commit does (a new build, an update of the previous build, or nothing), which modules to install, and which test tags to run. You can even build a development branch on another major Odoo version while you port modules for an upgrade.
Two habits keep development healthy:
- Keep branches short-lived. Development builds are removed a few days after they were built to free resources; a push or Rebuild brings them back.
- Write tests for the paths that hurt. Odoo's test framework runs on each development build. A test for "confirming a sale order creates the right delivery" is cheap to write and catches a surprising number of regressions.
Staging: a rehearsal on real data
When a change is ready, merge it into your staging branch (on Odoo Cloud you can drag one branch onto another to merge it on GitHub). The first build after a branch moves to staging copies production and neutralizes the copy:
- Outgoing emails are caught and shown in the branch's Mails tab instead of being sent.
- Incoming mail servers are disabled, so staging doesn't read your real mailbox.
- Scheduled actions are turned off, apart from Odoo's own database cleanup.
- Payment providers and shipping connectors are disabled or switched to test mode.
- The Enterprise subscription code is removed, so the copy never counts as a second production database.
On Odoo 16 and later this uses Odoo's own neutralization, which each installed app extends; on Odoo 14 and 15 the platform runs the equivalent SQL.
Later pushes to staging update the existing staging database the same way production will be updated. That is the point: a staging build is a rehearsal of the production deploy, including how long the update takes. When you want fresh data, Rebuild starts again from a new copy of production. You can also restore any production backup into a staging branch, which is the easiest way to investigate "what did this record look like yesterday?".
A word on your own integrations. Neutralization knows about Odoo's standard features. If a custom module calls an external API with credentials stored in the database, it may still call it from staging. Guard those calls, for example by checking the database.is_neutralized system parameter.
Production: deploy by version bump
Production keeps its database, so a push never reinstalls anything. On each production build, the platform compares the version in each module's __manifest__.py with the version installed in the database, and updates only the modules whose version went up.
{
"name": "My module",
"version": "17.0.1.0.3", # was 17.0.1.0.2: bumped so production runs the update
"depends": ["sale"],
}That gives you control over what a deploy does:
- No version change: the new code is deployed without a database update and without downtime. Good for fixes in Python logic that don't touch models, views or data files.
- Version bumped: the module is updated. Bump it whenever you change fields, views, security rules or data files; without a bump the new Python runs against an un-updated database. Production is stopped for the length of the update.
- New modules are not installed automatically on an existing production database: install them from Odoo's Apps menu when you are ready.
Rollback is automatic
Before updating modules, or when requirements.txt changed, Odoo Cloud takes an update backup of production. If the update fails, or the log contains errors, the backup is restored and the previous build starts again: production keeps running the old code on its old data. You read the log, fix the problem, and push again. The last five update backups stay in the Backups tab if you ever need to go back by hand.
A typical release, start to finish
- Branch from production, push, and let the development build run your tests.
- Open a pull request; the commit status shows whether the build passed.
- Merge into staging. The staging build updates the neutralized copy of production.
- Connect to staging and test with real data. Check the Mails tab for emails the change would have sent.
- Bump module versions where models, views or data changed.
- Merge staging into production at a quiet time. Watch the build log.
- If anything fails, production is rolled back automatically; fix and repeat from step 3.
Access follows the stages
Not everyone needs production. Collaborators get one of three roles: developers work on development branches, testers also reach staging, and admins manage production, backups and settings. Every Connect, shell command, stage move and restore is recorded in the audit logs. Push rights on the repository stay managed on GitHub.
Upgrades use the same pattern
A major version upgrade is just a bigger release, and it follows the same stages. On a staging branch, the Upgrade tab upgrades a copy of the latest production backup, without touching production. The branch then stays in upgrade mode: every push loads the upgraded database and updates your modules while you port them. When the build is green and your tests pass, you upgrade production, with a backup first and an automatic restore if anything fails.
Summary
- Development proves the code, staging proves it on your data, production only gets what staging has seen.
- Neutralized staging lets you test with real data without emailing customers or charging cards.
- Bump the manifest version to update a module in production; leave it to deploy code only.
- Update backups and automatic rollback make a failed deploy a short inconvenience rather than an outage.
Coming from Odoo.sh, this will feel familiar: see Odoo.sh alternatives. To try it on your own repository, see the pricing or the full feature list.
Odoo and Odoo.sh are trademarks of Odoo S.A. Odoo Cloud is an independent service, not affiliated with or endorsed by Odoo S.A.