A small set of DevOps utilities for Odoo deployments and local development.
The main entrypoint is odt-env — a CLI that provisions and syncs a reproducible Odoo workspace from an INI file.
- Clones and updates Odoo and addon repositories
- Generates a ready-to-run
odoo-server.conf - Generates helper scripts (run, test, backup/restore, update, …)
- Optionally provisions a Python virtual environment and a wheelhouse (useful for offline installs / CI builds)
odt-env is designed to be repeatable: re-running it should converge your workspace to what the INI file describes.
- Installation
odt-env
Using pip:
pip install odoo-devops-toolsOr using uv:
uv tool install --reinstall odoo-devops-toolsVerify:
odt-env --helpProvision and sync a reproducible Odoo workspace from an INI configuration file.
- git
- uv (Python package & project manager): https://docs.astral.sh/uv/
Optional (only for some helper scripts):
- PostgreSQL client tools (e.g.
pg_dump,psql) for backup/restore.
1) Typical local dev (sync + venv + configs/scripts)
odt-env /path/to/ROOT/odoo-project.ini --sync-all --create-venv2) Sync repositories only (no Python provisioning)
odt-env /path/to/ROOT/odoo-project.ini --sync-all3) CI / build machine: create wheelhouse (no install into venv)
odt-env /path/to/ROOT/odoo-project.ini --sync-all --create-wheelhouse4) Offline install from an existing wheelhouse
odt-env /path/to/ROOT/odoo-project.ini --create-venv --reuse-wheelhouse5) Hard rebuild of the venv
odt-env /path/to/ROOT/odoo-project.ini --sync-all --rebuild-venvIf you run no options, odt-env only regenerates configs and helper scripts:
odt-env /path/to/ROOT/odoo-project.ini-
ROOT is where the workspace is physically created (repos, venv, wheelhouse, generated files).
- Default: the directory containing the INI file.
- Override with
--root(must point to an existing directory).
-
DEST_ROOT controls what paths get embedded into generated files (configs/scripts).
- Default: same as ROOT.
- Override with
--dest-root(does not need to exist on the build machine).
A typical deployment workflow is: build under a temporary directory, but generate configs/scripts that reference the final location on the target host:
odt-env /path/to/odoo-project.ini --sync-all --create-wheelhouse \
--root /tmp/build-root \
--dest-root /srv/odoo/myprojectROOT is the directory containing odoo-project.ini (or the path passed via --root).
If you pass --dest-root, odt-env still writes files under ROOT, but paths embedded in generated files are based on DEST_ROOT.
Default layout:
ROOT/odoo/— Odoo repositoryROOT/odoo-addons/<name>/— addon repositoriesROOT/odoo-backups/— backups directoryROOT/odoo-data/— data directory (customizable via[config] data_dir)ROOT/odoo-configs/— generated config (e.g.odoo-server.conf)ROOT/odoo-scripts/— generated helper scriptsROOT/odoo-logs/— runtime logs (created byinstance.sh)ROOT/venv/— virtualenv / Python toolchain (when enabled)ROOT/wheelhouse/— wheel cache for offline installs (when enabled)
odt-env reads an INI file with these sections:
[virtualenv]— Python version, managed Python, requirements, constraints…[odoo]— Odoo repo + branch[addons.<name>]— addon repos (repeatable per addon)[config]— values rendered intoodoo-server.conf(db, ports, log settings, …)[include]— optional includes (config inheritance)
This example provisions an Odoo 18.0 workspace with two addon repositories:
[virtualenv]
python_version = 3.11
# Optional (default: true): when false, odt-env will NOT install a managed CPython via `uv python install`.
# Instead it will rely on an existing system Python that matches `python_version`.
# managed_python = false
build_constraints =
requirements =
requirements_ignore =
[odoo]
repo = https://github.com/odoo/odoo.git
branch = 18.0
# Optional: If true, keep repo as a shallow, single-branch clone (depth=1). If false (default), do a full clone/fetch.
shallow_clone = true
[addons.oca-web]
repo = https://github.com/OCA/web.git
branch = 18.0
[addons.oca-helpdesk]
repo = https://github.com/OCA/helpdesk.git
branch = 18.0
[config]
http_port = 8069
gevent_port = 8072
db_host = 127.0.0.1
db_port = 5432
db_name = sample_odoo18
db_user = sample_odoo18
db_password = sample_odoo18
log_level = debug
max_cron_threads = 0You can split configuration across multiple INI files (for example a shared base + environment-specific overrides).
Rules:
- paths are resolved relative to the INI file that declares the include,
- included files load first; the including file loads last (later values override earlier ones),
- prefix a file path with
?to make it optional (missing file is skipped).
Example:
odoo-base.ini:
[virtualenv]
python_version = 3.11
build_constraints =
requirements =
requirements_ignore =
[odoo]
repo = https://github.com/odoo/odoo.git
branch = 18.0
[config]
http_port = 8069
gevent_port = 8072
db_host = 127.0.0.1
db_port = 5432
db_name = sample_odoo18
db_user = sample_odoo18
db_password = sample_odoo18odoo-dev.ini:
[include]
files = odoo-base.ini
[odoo]
branch = develop
[config]
db_name = sample_odoo18_devodoo-test.ini:
[include]
files =
odoo-base.ini
?odoo-dev.ini
[config]
db_name = sample_odoo18_testodt-env uses Python ExtendedInterpolation, so you can reference values with ${section:option}.
It also injects workspace path variables into the INI DEFAULT scope (available from any section):
${ini_dir}— directory containing the INI file${root_dir}— workspace root directory${odoo_dir}—ROOT/odoo${addons_dir}—ROOT/odoo-addons${backups_dir}—ROOT/odoo-backups${configs_dir}—ROOT/odoo-configs${config_path}— full path to the generatedodoo-server.conf${scripts_dir}—ROOT/odoo-scripts${venv_python}— full path to the venv Python executable
Note on --dest-root:
odt-envalways builds under filesystem ROOT.- When
--dest-rootis provided, variables like${root_dir},${odoo_dir},${addons_dir}, … are evaluated against DEST_ROOT for the[config]section (so generatedodoo-server.confuses deployment paths). - Other sections (
[odoo],[addons.*],[virtualenv]) continue to use filesystem ROOT (so git/venv/wheelhouse operations work locally). ${ini_dir}always points to the directory of the entry INI file on the build machine (useful for includes).
Tip: create your own helper section (e.g. [vars]) and reuse it elsewhere:
[vars]
project = sample_odoo18
branch = 18.0
[virtualenv]
python_version = 3.11
requirements =
-r ${ini_dir}/requirements-dev.txt
[odoo]
repo = https://github.com/odoo/odoo.git
branch = ${vars:branch}
[addons.oca-web]
repo = https://github.com/OCA/web.git
branch = ${odoo:branch}
[config]
db_name = ${vars:project}
data_dir = ${root_dir}/odoo-data/${vars:project}
logfile = ${root_dir}/odoo-logs/${vars:project}.logodt-env always computes a base addons_path for odoo-server.conf that includes:
- the Odoo core addons directory (
ROOT/odoo/addonsand/orROOT/odoo/odoo/addons), - every synced addon repository (
ROOT/odoo-addons/<name>).
If you set addons_path in [config], it extends (appends to) the computed base list (it does not replace it). Duplicates are removed.
Format:
- comma-separated list and/or multi-line value,
- relative paths are resolved relative to
ROOT(same as${root_dir}).
Example:
[config]
addons_path =
odoo-addons/3rd_party_addons,
${addons_dir}/extra_addons,By default, repositories are kept as full clones (full history, branches, tags). For very large repositories (especially odoo/odoo), you can enable a shallow, single-branch workflow.
Where to set it:
[odoo] shallow_clone = true[addons.<name>] shallow_clone = true
Behavior:
- initial clone:
git clone --depth 1 --single-branch --branch <branch> … - sync/update: fetch only
origin <branch>with--depth 1, then hard-reset toorigin/<branch>
Pros:
- faster clone/fetch
- less disk usage
Limitations:
- depth is fixed to 1 (only the branch tip is available),
- operations requiring history (e.g. long
git log,git bisect,git describeon older tags) won’t work as expected, - switching from full → shallow does not automatically drop history; delete the repo directory and re-sync to realize the benefits.
Switching back to full:
- set
shallow_clone = false(or remove it) and re-run a sync;odt-envwill unshallow and widen the fetch refspec so laterfetch --all --tagscan pull all branches/tags.
Tip: The quick-start section covers the most common workflows. Use this section as a reference when you need to fine-tune behavior.
--root— workspace ROOT directory (default: directory containing the INI)--dest-root— deployment root for paths embedded in generated configs/scripts (default: same as ROOT)--no-configs— do not generate config files (e.g.odoo-server.conf)--no-scripts— do not generate helper scripts underROOT/odoo-scripts/--no-data-dir— do not create the Odoo data folder underROOT/odoo-data/(or custom[config] data_dir)
--sync-odoo— sync onlyROOT/odoo--sync-addons— sync onlyROOT/odoo-addons/*(no-op if no[addons.*]sections exist)--sync-all— sync both Odoo + addons
--create-venv— create/updateROOT/venvand install Python dependencies (from wheelhouse)--rebuild-venv— delete + recreateROOT/venv(implies--create-venv)--create-wheelhouse— build/update the lock +ROOT/wheelhouse/without installing requirements into venv
(may still create/updateROOT/venv/as a Python toolchain)--reuse-wheelhouse— reuse an existingROOT/wheelhouse/and install strictly offline (requires--create-venv)--clear-pip-wheel-cache— remove all items from pip’s wheel cache
Python dependencies are installed into the venv only when you pass --create-venv (or --rebuild-venv).
- venv location:
ROOT/venv - Python version:
[virtualenv] python_version - tooling:
uv venvanduv pip - wheelhouse location:
ROOT/wheelhouse/ - installs use offline mode from wheelhouse (
--offline --no-index)
If ROOT/wheelhouse/ is already prepared:
odt-env /path/to/odoo-project.ini --create-venv --reuse-wheelhouseThis skips lock compilation and wheel building and performs a strict offline install from the existing wheelhouse. You can combine it with --sync-all/--sync-odoo/--sync-addons (repo sync still happens; deps install is offline).
By default, odt-env manages the requested CPython version via uv:
- Option:
[virtualenv] managed_python - Default:
true
Behavior:
managed_python = true: when creatingROOT/venv/,odt-envensures the requested Python exists by runninguv python install.managed_python = false:odt-envskipsuv python installand relies on an already-installed system Python that matchespython_version.
Example:
[virtualenv]
python_version = 3.11
managed_python = falseScripts are generated into ROOT/odoo-scripts/. All scripts:
- use the generated config file (
ROOT/odoo-configs/odoo-server.conf), - forward extra CLI arguments to the underlying command.
Linux/macOS:
run.sh— start Odoo in the foregroundinstance.sh— manage Odoo as a background service (start/stop/restart/status) and log toROOT/odoo-logs/odoo-server.logtest.sh— run testsshell.sh— open an interactive Odoo shellinitdb.sh— initialize databasebackup.sh— create a timestamped ZIP backup (DB + filestore) intoROOT/odoo-backups/restore.sh— restore a backup ZIPrestore_force.sh— restore and overwrite an existing DBupdate.sh— update modules, auto-detecting addons to update using file-content hashes stored in the DBupdate_all.sh— force a full upgrade (-u base)
Windows:
run.bat,test.bat,shell.bat,initdb.bat,backup.bat,restore.bat,restore_force.bat,update.bat,update_all.bat
Start the server:
./odoo-scripts/run.shodoo-scripts\run.batManage the background instance (Linux only):
./odoo-scripts/instance.sh start
./odoo-scripts/instance.sh status
./odoo-scripts/instance.sh restart
./odoo-scripts/instance.sh stopUpdate modules:
./odoo-scripts/update.shodoo-scripts\update.batRun tests:
./odoo-scripts/test.sh -u base
./odoo-scripts/test.sh -u mail,web
./odoo-scripts/test.sh -u my_custom_addonodoo-scripts\test.bat -u base
odoo-scripts\test.bat -u mail,web
odoo-scripts\test.bat -u my_custom_addonBackup / restore:
./odoo-scripts/backup.sh
./odoo-scripts/restore.sh PATH/TO/BACKUP.zip
./odoo-scripts/restore_force.sh PATH/TO/BACKUP.zipodoo-scripts\backup.bat
odoo-scripts\restore.bat PATH\TO\BACKUP.zip
odoo-scripts\restore_force.bat PATH\TO\BACKUP.zipIf any target repository (Odoo or an addon) contains local uncommitted changes (including untracked files), odt-env aborts. Commit/stash/clean your working tree before running odt-env.