Build reproducible Odoo workspaces.
odoo-compose, provided by the odoo-devops-tools package, builds and maintains reproducible Odoo workspaces from a single project definition.
A basic workspace definition with addons can look like this:
[odoo]
version = 19.0
[addons.oca-web]
repo = https://github.com/OCA/web.git
branch = ${odoo:version}
[addons.oca-helpdesk]
repo = https://github.com/OCA/helpdesk.git
branch = ${odoo:version}From this configuration, odoo-compose can generate a workspace such as:
ROOT/
├── docker/ # generated Docker artifacts
│ ├── local/ # Docker workflow artifacts
│ │ └── configs/ # generated local Odoo config
│ └── deploy/ # self-contained deploy build context
├── odoo/ # Odoo source
├── odoo-addons/ # addon sources
│ ├── oca-web/
│ └── oca-helpdesk/
├── odoo-configs/ # generated Odoo configuration
├── odoo-data/ # Odoo data directory
├── odoo-logs/ # runtime logs
├── odoo-backups/ # backups created by CLI commands
├── wheelhouse/ # offline Python wheelhouse
├── venv/ # Python virtual environment
├── compose.yaml # Docker Compose file
└── odoo-project.ini # workspace configuration
- Reproducible workspaces — define and recreate an Odoo workspace from a single INI configuration.
- Docker workflows — generate local Docker Compose environments with optional Mailpit mail testing and self-contained deploy build contexts.
- Portable workflows — create verified bundles with sources and Python wheels for reproducible or offline use.
- Python dependency management — manage Python versions, virtual environments and requirements.
- Composable configuration — combine INI layers, variables, and CLI overrides.
odoo-compose focuses on creating and maintaining the Odoo workspace and its derived artifacts. Infrastructure provisioning and deployment orchestration remain outside its scope.
- git: https://git-scm.com/install/
- uv: https://docs.astral.sh/uv/getting-started/installation/
- Docker: https://docs.docker.com/get-docker/ — optional, for Docker workflows.
Install the CLI with uv:
uv tool install -U odoo-devops-toolsVerify the installation:
odoo-compose --helpBoth odoo-compose and odoo-compose-venv support TAB completion for commands,
options, and filesystem paths. No additional Python dependencies are required.
To enable shell autocompletion, run the commands for your shell:
Bash
echo 'eval "$(odoo-compose completion bash)"' >> ~/.bashrc
echo 'eval "$(odoo-compose-venv completion bash)"' >> ~/.bashrcZsh
echo 'eval "$(odoo-compose completion zsh)"' >> ~/.zshrc
echo 'eval "$(odoo-compose-venv completion zsh)"' >> ~/.zshrcZsh's completion system (compinit) must be enabled before these lines run.
Fish
mkdir -p ~/.config/fish/completions
odoo-compose completion fish > ~/.config/fish/completions/odoo-compose.fish
odoo-compose-venv completion fish > ~/.config/fish/completions/odoo-compose-venv.fishPowerShell
if (!(Test-Path -Path $PROFILE)) {
New-Item -ItemType File -Path $PROFILE -Force | Out-Null
}
Add-Content -Path $PROFILE -Value 'odoo-compose completion powershell | Out-String | Invoke-Expression'
Add-Content -Path $PROFILE -Value 'odoo-compose-venv completion powershell | Out-String | Invoke-Expression'Restart your shell or reload its configuration to activate completion. For example,
odoo-compose setup --sy<TAB> suggests matching options such as --sync,
--sync-odoo, --sync-addons, and --sync-all.
odoo-devops-tools supports two development workflows, each with its own CLI command:
- Docker workflow (
odoo-compose) — run Odoo and PostgreSQL with the generated Docker Compose environment. - Native development with venv (
odoo-compose-venv) — run Odoo directly on the host using a generated Python virtual environment.
Choose the workflow that fits your environment. Both use the same odoo-project.ini project definition.
Create a new workspace:
odoo-compose setup ./odoo19This creates ./odoo19/odoo-project.ini with this initial content:
[odoo]
version = 19.0Then prepare and start the Docker Compose environment:
cd ./odoo19
odoo-compose up --buildOptionally initialize the Odoo database from the command line instead of through the web interface:
odoo-compose run -i base --without-demo=all --stop-after-initDocker Compose runs both Odoo and PostgreSQL. Odoo is available at http://localhost:8069.
Note By default, generated Compose services use
restart: unless-stopped; to prevent a development stack from automatically starting after a host or Docker restart, run:odoo-compose setup --set docker:restart=no odoo-compose up --build
To extend Odoo with additional functionality, add extra addons through [addons.<name>] sections in odoo-project.ini.
In this example, we add two Git-based addon repositories, OCA/web and OCA/helpdesk:
[addons.oca-web]
repo = https://github.com/OCA/web.git
branch = ${odoo:version}
[addons.oca-helpdesk]
repo = https://github.com/OCA/helpdesk.git
branch = ${odoo:version}Sync the addon repositories and regenerate the Docker environment:
odoo-compose setup --sync
odoo-compose up --buildThe repositories are cloned into ROOT/odoo-addons/ and bind-mounted into the Odoo container. Python dependencies from their requirements.txt files are included when rebuilding the Docker image.
Install the new modules:
odoo-compose run -i web_notify,helpdesk_mgmt --without-demo=all --stop-after-initFor subsequent addon updates, use:
odoo-compose updateBy default, the generated Docker workflow runs the Odoo provided by [docker].base_image.
Use odoo_source = workspace when you need to run Odoo from the workspace source.
For example:
[odoo]
version = 19.0
repo = git@github.com:my-company/odoo.git
branch = 19.0-custom-odoo
[docker]
odoo_source = workspaceSync the Odoo source and generate the Docker artifacts:
odoo-compose setup --sync
odoo-compose up --buildThe resolved Odoo source is bind-mounted read-only at /opt/odoo. The Odoo Python source from the base image is removed while its installed dependencies remain available.
Enable Mailpit in the local Docker workflow when you need to inspect or test email without sending it to a real SMTP server:
odoo-compose setup --set docker:mailpit_enabled=true
odoo-compose upMailpit is available at http://localhost:8025 by default. When running multiple workspaces, override the host web UI port, for example:
odoo-compose setup --set docker:mailpit_enabled=true --set docker:mailpit_webui_port=8026
odoo-compose upThe generated local Odoo configuration sends outgoing email to mailpit:1025. When Mailpit is enabled, local Docker SMTP connection settings (smtp_server, smtp_port, smtp_ssl, smtp_user, and smtp_password) are intentionally overridden; the deploy configuration keeps the values from [config] unchanged.
Mailpit also exposes POP3 inside the Compose network at mailpit:1110, using odoo / odoo credentials. This can be used to test Odoo incoming mail and fetchmail flows by configuring an Odoo Incoming Mail Server with POP3, server mailpit, port 1110, and SSL/TLS disabled.
Mailpit is generated only for the local Docker workflow and is not included in the deploy build context.
Use [docker.share.<name>] sections to expose host directories to the Odoo container as read-write bind mounts:
[docker.share.exchange]
source = /srv/integration/exchange
target = /mnt/exchangesource may be an absolute host path or a path relative to the workspace root. target must be an absolute path inside the Odoo container.
odoo-compose does not create the source directory or change its ownership or permissions; host provisioning or the deployment process must prepare the directory before Docker starts.
You can optionally set CPU and memory limits for each service in the generated compose.yaml using [docker] in odoo-project.ini:
[docker]
odoo_cpus = 2.0
odoo_mem_limit = 4g
db_cpus = 1.0
db_mem_limit = 2g
mailpit_enabled = true
mailpit_cpus = 0.5
mailpit_mem_limit = 256mEach option is independent and optional. If an option is missing or empty, odoo-compose omits that limit from the Compose service; no CPU or memory limit is imposed by the generator. CPU values must be positive decimal numbers (such as 0.5 or 2.0). Memory limits must be positive amounts (such as 256m, 2g, or 4g); zero and negative values are rejected. A memory limit is a hard container limit: exceeding it may cause an out-of-memory termination, so size it for your workload.
Apply the updated resource limits and start the services. up regenerates the Compose configuration automatically, so no separate setup or image rebuild is necessary:
odoo-compose upUse --create-docker-deploy to generate a self-contained Docker build context for CI/CD, testing, staging, production, or another non-local deployment workflow.
Addon modules are staged into the build context so the resulting image does not depend on bind-mounted workspace sources:
odoo-compose setup --sync --create-docker-deployWhen [docker].odoo_source = workspace, sync the Odoo source as well:
odoo-compose setup --sync --create-docker-deployThis additionally creates:
ROOT/docker/deploy/
├── Dockerfile
├── .dockerignore
├── addons/
├── odoo/ # only with odoo_source = workspace
├── requirements/
└── configs/
└── odoo.conf
Addon modules are staged under docker/deploy/addons/ and copied into /mnt/extra-addons/ by the generated Dockerfile.
With odoo_source = workspace, the resolved Odoo source is also staged under docker/deploy/odoo/ and copied into /opt/odoo/, so the deploy image is self-contained.
In this workflow, Odoo runs directly on the host using a generated Python virtual environment.
Create the workspace with the Odoo source and virtual environment:
odoo-compose-venv setup ./odoo19 --sync --create-venv \
--set config:db_host=127.0.0.1 \
--set config:db_name=odoo \
--set config:db_user=odoo \
--set config:db_password=odooNote Make sure PostgreSQL is running at the configured host and the configured database user exists.
This creates ./odoo19/odoo-project.ini with this initial content:
[odoo]
version = 19.0
[config]
db_host = 127.0.0.1
db_name = odoo
db_user = odoo
db_password = odooThen start Odoo in the background:
cd ./odoo19
odoo-compose-venv up
odoo-compose-venv statusOdoo starts with the generated configuration from ./odoo-configs/odoo-server.conf and is available at http://localhost:8069.
Use odoo-compose-venv run instead to run in the foreground:
cd ./odoo19
odoo-compose-venv runTo extend Odoo with additional functionality, add extra addons through [addons.<name>] sections in odoo-project.ini.
In this example, we add two Git-based addon repositories, OCA/web and OCA/helpdesk:
[addons.oca-web]
repo = https://github.com/OCA/web.git
branch = ${odoo:version}
[addons.oca-helpdesk]
repo = https://github.com/OCA/helpdesk.git
branch = ${odoo:version}Sync the addon repositories and recreate the Python environment to include their dependencies:
odoo-compose-venv setup --sync --create-venvThe repositories are cloned into ROOT/odoo-addons/ and automatically added to the generated addons_path. Python dependencies from their requirements.txt files are included in the virtual environment.
Install the new modules:
odoo-compose-venv run -i web_notify,helpdesk_mgmtFor subsequent addon updates, use:
odoo-compose-venv updateThe update command uses click-odoo-update with the workspace Odoo configuration.
By default, odoo-compose uses uv to install and manage the requested Python version.
If you already have a suitable system Python installed, you can disable managed Python.
Disable managed Python by adding python_version = 3.11 and managed_python = false to the odoo-project.ini file.
Note Set
python_versionto the Python version you want to use from your local system. In the example below, 3.11 is only illustrative.
[virtualenv]
managed_python = false
python_version = 3.11After changing the project file, run odoo-compose again from the workspace root:
odoo-compose-venv setup --sync --create-venvThis recreates the virtual environment at ROOT/venv using the system Python.
The [virtualenv] section controls additional Python dependencies used when provisioning both the native virtual environment and generated Docker images.
Use it to add new packages, pin specific versions, and override packages collected from Odoo or addon repository requirements.txt files.
Use:
requirementsto add extra packages or pin an explicit versionconstraintsto restrict dependency versions without installing packages by themselvesbuild_constraintsto restrict build-time dependency versionsrequirements_ignoreto skip packages that would otherwise be collected from repository requirements files
When a package is listed in requirements, odoo-compose automatically gives that package priority over the same package name from collected repository requirements and constraints. This means you can usually pin a package version just by adding it to requirements.
Use requirements to install additional packages or to force a specific version:
[virtualenv]
requirements =
requests==2.32.3
boto3==1.35.99In this example, both packages are included in the generated dependency set and pinned to the specified versions.
Use constraints to restrict versions selected by the dependency resolver without adding those packages to the installation set:
[virtualenv]
constraints =
urllib3<2
lxml<6A constraint only applies when the package is required by another dependency. If the same package is listed explicitly in requirements, the explicit requirement takes priority.
If you want to replace a package with a different distribution name, add the replacement to requirements and skip the original package with requirements_ignore.
Example:
[virtualenv]
requirements =
psycopg2-binary==2.9.9
requirements_ignore =
psycopg2In this example, odoo-compose installs psycopg2-binary==2.9.9 and skips psycopg2 when collecting repository requirements.
Portable bundles are useful when you want to prepare an Odoo workspace on an internet-connected machine and reproduce it on another compatible machine without cloning repositories or downloading Python packages again.
A portable workspace bundle is a ZIP archive containing:
odoo/
odoo-addons/
wheelhouse/
manifest.json
odoo-project.ini
The bundle does not contain the virtual environment, database data, logs, backups, generated configuration files, Docker artifacts, Git metadata, or provisioning history.
Those machine-specific outputs are recreated on the target machine.
On an internet-connected build machine, sync the sources, build the wheelhouse, and create the bundle in one command:
odoo-compose-venv setup --sync --create-venv --create-bundleWhen no output path is supplied, the bundle is written to:
ROOT/dist/ROOT-NAME.odt.zip
You can also select an explicit output file:
odoo-compose-venv setup --sync --create-venv --create-bundle ./artifacts/odoo18-production.odt.zipBy default, bundle creation stops when a bundled Git repository has uncommitted changes.
To intentionally include those changes in the bundle, use:
odoo-compose-venv setup --sync --create-venv --create-bundle --allow-dirty-bundleCopy the ZIP to the target machine and import it into an empty directory:
odoo-compose-venv setup --create-from-bundle ./odoo18-production.odt.zip \
--root ./odoo18-prod \
--set config:db_host=127.0.0.1 \
--set config:db_name=odoo \
--set config:db_user=odoo \
--set config:db_password=odooThe import operation:
- verifies the bundle format, platform, and CPU architecture;
- rejects unsafe ZIP paths, duplicate entries, and symbolic links;
- verifies every bundled file using its size and SHA-256 checksum;
- extracts Odoo, addons, the sanitized project INI, and the wheelhouse;
- recreates
ROOT/venvstrictly from the bundled wheelhouse; - regenerates workspace configuration files.
ROOT must be empty. If --root is omitted, the current working directory is used and must be empty. The imported manifest is saved as ROOT/.odt-env/imported-bundle-manifest.json.
Compatibility note A wheelhouse is platform- and architecture-dependent. Create and import a bundle on compatible systems, for example Linux x86-64 to Linux x86-64. The target machine must have
uvand access to the configured Python version. When[virtualenv].managed_python = true,uvmay still need network access if that Python interpreter is not already installed or cached. For a fully disconnected target, install the required Python interpreter beforehand or usemanaged_python = false.
The existing manual workflow remains available. After preparing a complete workspace on the build machine, copy the whole workspace and run this command from the copied root:
odoo-compose-venv setup --create-venv-from-wheelhouseThis recreates ROOT/venv, skips dependency compilation and wheelhouse building, and installs strictly from the existing ROOT/wheelhouse/ and all-requirements.lock.txt.
Use the same lifecycle commands with either CLI after provisioning the workspace with setup:
# Docker: generate Docker config and start the Compose stack
odoo-compose up --build
odoo-compose status
odoo-compose down
# Native: generate config and start Odoo in the background
odoo-compose-venv up
odoo-compose-venv status
odoo-compose-venv downodoo-compose up regenerates local workspace artifacts and then runs the equivalent of docker compose up -d --wait.
odoo-compose up --build additionally builds the images (docker compose up -d --build --wait).
Container orchestration remains with Docker Compose, and its CLI can still be used directly for advanced or service-specific operations.
up requires an existing odoo-project.ini. It regenerates local configuration without fetching Git repositories or recreating virtualenvs.
odoo-compose operates on Docker workspaces; odoo-compose-venv operates on native Python virtualenv workspaces. Both use ROOT/odoo-project.ini, but generate their own workflow artifacts. Commands operate directly through the installed CLI; generated wrapper scripts are not required.
Running either CLI without a command displays help; workspace changes are performed through setup. Use odoo-compose <command> --help or odoo-compose-venv <command> --help for command-specific help. The global --version option shows the installed CLI version. Use --root ROOT to select a workspace outside the current directory where supported.
setupCreate or update a workspace, optionally synchronizing sources and generating artifacts.
upRegenerate workspace artifacts and start Odoo in the background.
downStop the runtime without deleting data.
statusShow the current runtime state.
runRun Odoo with additional arguments.
shellOpen an Odoo shell.
updateUpdate installed Odoo modules.
testRun Odoo tests.
backup-dbCreate a PostgreSQL dump.
restore-dbRestore a PostgreSQL dump.
backup-filestoreArchive the database filestore.
restore-filestoreRestore a filestore archive.
resticRun a Restic subcommand.
backup-filestore-resticSnapshot a filestore with Restic.
restore-filestore-resticRestore a filestore from Restic.
neutralizeNeutralize a restored database and run optional SQL.
completionGenerate terminal completion definitions.
Create or update an Odoo workspace. When no project file exists, setup creates the default ROOT/odoo-project.ini; otherwise it reuses the existing configuration. Synchronizing repositories, building a virtual environment, creating bundles, and generating Docker artifacts are optional.
Usage
{odoo-compose|odoo-compose-venv} setup [ROOT|INI] [OPTIONS]
Arguments
ROOT|INIOptional workspace directory, existing local project INI file, Git-based INI source, or URL. A directory is created if needed; an existing INI is preserved. When omitted, the current directory is the workspace root. An existing file or path ending in
.iniis interpreted as an INI source.
Alternatively, load the workspace definition from a local INI file, Git repository, or URL:
# Docker workflow:
odoo-compose setup /path/to/odoo-project.ini
odoo-compose setup 'git+git@github.com:company/repo.git//examples/odoo-project.ini?ref=main'
odoo-compose setup 'https://github.com/lck/odoo-devops-tools/blob/main/examples/odoo-project.ini'
# Native workflow:
odoo-compose-venv setup /path/to/odoo-project.ini
odoo-compose-venv setup 'git+git@github.com:company/repo.git//examples/odoo-project.ini?ref=main'
odoo-compose-venv setup 'https://github.com/lck/odoo-devops-tools/blob/main/examples/odoo-project.ini'Git source syntax: git+REPO_URL//PATH/TO/PROJECT.ini?ref=REF.
Options
Paths and configuration
--rootROOTWorkspace root. By default, uses the provided
ROOT, the local INI's directory, or the current directory for remote/omitted INI sources. Cannot be combined with a positional workspace directory.-i,--includeINIInclude another INI layer; repeatable, with later layers taking precedence.
-e,--extra-varKEY=VALUEOverride or inject a
[vars]value; repeatable.-S,--setSECTION:KEY=VALUEOverride or create supported project settings; repeatable. Structured sections accept documented keys;
[config]accepts standard Odoo options exceptaddons_path.--no-configsSkip generating config files (Venv workflow).
--no-data-dirSkip creation of the Odoo data directory.
--no-provisioning-logDo not write provisioning metadata under
ROOT/.odt-env/.--show-last-runShow
ROOT/.odt-env/last-provisioning.jsonand exit without provisioning.
Repository synchronization
--syncAutomatically synchronize only the Git sources used by the selected workflow. In Docker, Odoo is synced only with
[docker].odoo_source = workspace; in Venv, Git-managed Odoo is synced. Both workflows sync configured Git addons.--sync-odooSync Git-managed Odoo sources; reuse
[odoo].pathwithout Git sync when a local path is configured.--sync-addonsSync Git-managed addons into
ROOT/odoo-addons/.--sync-allExplicitly select both Odoo and addon sources, regardless of the selected workflow. Unlike
--sync, this can fetch Odoo sources even when Docker runs Odoo from the base image.
Note: Repository sync aborts if a target Git repository has uncommitted changes. Commit, stash or discard changes before syncing. The four sync switches are mutually exclusive; without any of them,
setuponly regenerates artifacts and does not fetch repositories.
Virtualenv and bundles (odoo-compose-venv only where indicated)
--create-venvExplicitly create or recreate
ROOT/venvand refresh the wheelhouse (Venv only). Without a virtualenv-provisioning option, the environment is not recreated.--create-venv-from-wheelhouseRecreate
ROOT/venvoffline fromROOT/wheelhouse/andall-requirements.lock.txt, without compiling dependencies or rebuilding the wheelhouse.--clear-pip-wheel-cacheRemove all items from pip's wheel cache.
--create-bundle[BUNDLE]Create a verified portable ZIP containing sources, a sanitized project INI and the wheelhouse (Venv only). Default output:
ROOT/dist/ROOT-NAME.odt.zip. Explicit relative paths are resolved from the current directory.--allow-dirty-bundleAllow bundle creation from repositories with uncommitted changes; otherwise dirty repositories abort the operation.
--create-from-bundleBUNDLEVerify and import a bundle into an empty
ROOTand recreate the virtualenv from its wheelhouse (Venv only). Does not generate Docker artifacts and cannot be combined with--create-docker-deploy.
Docker generation
--create-docker-deployGenerate a self-contained deployment build context under
ROOT/docker/deploy/(Docker only).
Examples
# Docker workflow:
odoo-compose setup ./odoo19
odoo-compose setup ./odoo18 --set odoo:version=18.0
# Native workflow:
odoo-compose-venv setup ./odoo19 --sync --create-venv
odoo-compose-venv setup ./odoo18 --set odoo:version=18.0 --sync-all --create-venvPrepare and start the selected workspace. Requires an existing odoo-project.ini. Docker regenerates the local Docker build context, Odoo config and compose.yaml, then starts services with Docker Compose; it waits for health checks. Native regenerates its configuration and starts Odoo in the background using the existing virtualenv. Neither variant automatically synchronizes repositories or rebuilds the native virtualenv.
Usage
{odoo-compose|odoo-compose-venv} up [--root ROOT]
odoo-compose up [--root ROOT] [--build] [--recreate]
Options
--rootROOTSelect an existing workspace (default: current directory).
--buildBuild Docker images before starting the services (Docker only).
--recreateForce recreation of the Odoo container, without forcing recreation of the database container (Docker only).
Notes
Docker up remembers the last applied generated Odoo configuration and recreates the Odoo container when it changes, including after a separate setup.
Running up without --build does not guarantee that changed Python dependencies or Dockerfile instructions are built into the image.
Native up uses ROOT/.odt-env/odoo-process.json for process identity and writes runtime output to ROOT/odoo-logs/odoo-up.log; it restarts the tracked process if the generated config changes.
Stop the selected workspace, preserving data. Docker delegates to docker compose down without --volumes; native gracefully stops only the verified Odoo process created by odoo-compose-venv up.
Usage
{odoo-compose|odoo-compose-venv} down [--root ROOT]
Options
--rootROOTSelect an existing workspace (default: current directory).
Notes
Native down never terminates arbitrary processes found by name or port. It does not stop PostgreSQL or a separately managed systemd service. Docker down removes Compose-managed containers and networks, but retains named volumes.
Show runtime status without provisioning or changing the workspace. Docker displays the Docker Compose service table; native reports the tracked Odoo PID and whether the process is still running.
Usage
{odoo-compose|odoo-compose-venv} status [--root ROOT]
Options
--rootROOTSelect an existing workspace (default: current directory).
Notes
Native status verifies the recorded process identity to avoid confusing a reused PID with Odoo. It does not probe the Odoo HTTP endpoint or report application health.
Run Odoo directly from the selected workspace. Docker uses a one-off container; Venv uses the existing virtualenv and generated odoo-server.conf.
Usage
{odoo-compose|odoo-compose-venv} run [--root ROOT] [ODOO_ARGS...]
Arguments
ODOO_ARGS...Additional options forwarded to Odoo.
Options
--rootROOTSelect a workspace.
Examples
# Docker workflow:
odoo-compose run -i base --stop-after-init
# Native workflow:
odoo-compose-venv run -i base --stop-after-initOpen an interactive Odoo shell. Like run, this uses a one-off container for Docker or the existing virtualenv for native workspaces.
Usage
{odoo-compose|odoo-compose-venv} shell [--root ROOT] [ODOO_ARGS...]
Arguments
ODOO_ARGS...Additional options forwarded to Odoo.
Options
--rootROOTSelect a workspace.
Examples
# Docker workflow:
odoo-compose shell
# Native workflow:
odoo-compose-venv shellUpdate installed Odoo addons through click-odoo-update.
Usage
{odoo-compose|odoo-compose-venv} update [--root ROOT] [--all] [ODOO_ARGS...]
Arguments
ODOO_ARGS...Additional arguments forwarded to
click-odoo-update.
Options
--rootROOTSelect a workspace.
--allInvoke
click-odoo-update --update-all.
Examples
# Docker workflow:
odoo-compose update --all
# Native workflow:
odoo-compose-venv update --allRun Odoo with --test-enable --stop-after-init in either workflow.
Usage
{odoo-compose|odoo-compose-venv} test [--root ROOT] [ODOO_ARGS...]
Arguments
ODOO_ARGS...Odoo options, such as modules and test tags.
Options
--rootROOTSelect a workspace.
Examples
# Docker workflow:
odoo-compose test -i sale --test-tags /sale
# Native workflow:
odoo-compose-venv test -i sale --test-tags /saleDocker note: Docker commands use one-off containers. Set
ODOO_DOCKER_SUDO=1on Linux if Docker requiressudo.
Create a PostgreSQL custom-format database dump.
Usage
{odoo-compose|odoo-compose-venv} backup-db [--root ROOT] [OUTPUT]
Arguments
OUTPUTOptional destination; defaults to
ROOT/odoo-backups/<database>_<timestamp>.dump.
Options
--rootROOTSelect a workspace.
Examples
# Docker workflow:
odoo-compose backup-db
# Native only: select a specific version of the PostgreSQL client tools
export POSTGRES_BIN=/usr/lib/postgresql/16/bin
odoo-compose-venv backup-dbNative PostgreSQL configuration: The database name comes from [config].db_name in odoo-project.ini; if missing, provisioning generates a workspace-specific name and saves it there. Connection details come from [config] or the standard PG* environment variables (PGHOST, PGPORT, PGUSER, PGPASSWORD, etc.), with .pgpass also supported. By default, client executables are found through PATH; POSTGRES_BIN selects their directory (pg_dump, pg_restore, psql, createdb, dropdb) for odoo-compose-venv only, at command runtime.
For Docker, the database name defaults to odoo when not specified.
Restore a PostgreSQL custom-format dump into [config].db_name (Docker defaults to odoo). Stop Odoo before restoring its active database.
Usage
{odoo-compose|odoo-compose-venv} restore-db [--root ROOT] [--force] [--copy|--move] BACKUP.dump
Arguments
BACKUP.dumpPath to the PostgreSQL dump to restore.
Options
--rootROOTSelect a workspace.
--forceAllow replacing an existing target database.
--copyRegenerate Odoo database identity and reset selected enterprise metadata (default).
--movePreserve the restored database's original identity.
Examples
# Docker workflow:
odoo-compose restore-db ./odoo-backups/pre-upgrade.dump
odoo-compose restore-db --force --copy ./odoo-backups/pre-upgrade.dump
odoo-compose restore-db --move ./odoo-backups/pre-upgrade.dump
# Native only: optionally override PostgreSQL client location
export POSTGRES_BIN=/usr/lib/postgresql/16/bin
odoo-compose-venv restore-db ./odoo-backups/pre-upgrade.dump
odoo-compose-venv restore-db --force --copy ./odoo-backups/pre-upgrade.dump
odoo-compose-venv restore-db --move ./odoo-backups/pre-upgrade.dumpAn existing target database is never replaced without --force. For native connection and client configuration, see backup-db.
Archive the selected database's filestore as a .tar.gz file.
Usage
{odoo-compose|odoo-compose-venv} backup-filestore [--root ROOT] [OUTPUT]
Arguments
OUTPUTOptional archive destination; defaults to
ROOT/odoo-backups/<database>_<timestamp>_filestore.tar.gz.
Options
--rootROOTSelect a workspace.
Examples
# Docker workflow:
odoo-compose backup-filestore
# Native workflow:
odoo-compose-venv backup-filestoreFor native workspaces, the filestore path comes from [config].data_dir or defaults to ROOT/odoo-data. Docker accesses the filestore through a one-off Odoo container.
Restore the selected database's filestore from a .tar.gz archive. Stop Odoo before restoring an active filestore.
Usage
{odoo-compose|odoo-compose-venv} restore-filestore [--root ROOT] [--force] FILESTORE.tar.gz
Arguments
FILESTORE.tar.gzFilestore archive to restore.
Options
--rootROOTSelect a workspace.
--forceAllow replacing an existing filestore.
Examples
# Docker workflow:
odoo-compose restore-filestore ./odoo-backups/pre-upgrade-filestore.tar.gz
odoo-compose restore-filestore --force ./odoo-backups/pre-upgrade-filestore.tar.gz
# Native workflow:
odoo-compose-venv restore-filestore ./odoo-backups/pre-upgrade-filestore.tar.gz
odoo-compose-venv restore-filestore --force ./odoo-backups/pre-upgrade-filestore.tar.gzFor native workspaces, the filestore path comes from [config].data_dir or defaults to ROOT/odoo-data. Docker accesses the filestore through a one-off Odoo container.
Run Restic against the configured repository. Restic supports incremental filestore snapshots and deduplication.
Usage
{odoo-compose|odoo-compose-venv} restic [--root ROOT] RESTIC_COMMAND [ARGS...]
Arguments
RESTIC_COMMANDRestic subcommand, such as
init,snapshotsorcheck.ARGS...Additional Restic arguments.
Options
--rootROOTSelect a workspace.
Examples
export RESTIC_REPOSITORY=/srv/restic/odoo
export RESTIC_PASSWORD_FILE=/etc/restic/odoo-password
# Docker workflow:
odoo-compose restic init
odoo-compose restic snapshots
odoo-compose restic check
# Native only: select a Restic executable instead of using PATH
export RESTIC_BIN=/opt/restic/restic
odoo-compose-venv restic init
odoo-compose-venv restic snapshots
odoo-compose-venv restic checkConfiguration: Set RESTIC_REPOSITORY and either RESTIC_PASSWORD_FILE or RESTIC_PASSWORD. Docker invokes Restic in a one-off container and bind-mounts an absolute local host repository; remote Restic repositories are also supported. For native workspaces, Restic is found through PATH by default; RESTIC_BIN sets the executable path at command runtime and applies only to odoo-compose-venv.
Back up the selected database's filestore as a Restic snapshot.
Usage
{odoo-compose|odoo-compose-venv} backup-filestore-restic [--root ROOT] [RESTIC_ARGS...]
Arguments
RESTIC_ARGS...Additional Restic backup arguments.
Options
--rootROOTSelect a workspace.
Examples
# Docker workflow:
odoo-compose backup-filestore-restic
# Native only: optional Restic executable override
export RESTIC_BIN=/opt/restic/restic
odoo-compose-venv backup-filestore-resticSet the repository and password as described under restic. Snapshots are tagged odoo, filestore and db:<database>. Set RESTIC_BACKUP_ID for an additional backup-id:<value> tag; RESTIC_HOST overrides the default host used to group snapshots.
Restore the selected database's filestore from a Restic snapshot. Stop Odoo before restoring an active filestore.
Usage
{odoo-compose|odoo-compose-venv} restore-filestore-restic [--root ROOT] [--force] [SNAPSHOT]
Arguments
SNAPSHOTSnapshot to restore, such as
latestor a snapshot ID.
Options
--rootROOTSelect a workspace.
--forceAllow restoring over an existing filestore.
Examples
# Docker workflow:
odoo-compose restore-filestore-restic --force latest
# Native workflow:
odoo-compose-venv restore-filestore-restic --force latestSet RESTIC_SOURCE_DB_NAME to restore a snapshot belonging to another database. When selecting latest, snapshot lookup also filters by host and the original filestore path. For repository configuration, host/tag settings and RESTIC_BIN, see restic and backup-filestore-restic.
Neutralize a restored production database for development or testing, then execute optional workspace SQL. Both workflows read ROOT/odoo-project.ini and do not regenerate workspace artifacts.
Usage
{odoo-compose|odoo-compose-venv} neutralize [--root ROOT] [--dry-run [OUTPUT_DIR]]
Options
--rootROOTSelect a workspace outside the current directory.
--dry-run[OUTPUT_DIR]Preview interpolated custom SQL without connecting to Odoo, Docker or PostgreSQL; optional output directory.
Examples
# Docker workflow:
odoo-compose neutralize
odoo-compose neutralize --dry-run
odoo-compose neutralize --dry-run ./preview-sql
# Native workflow:
odoo-compose-venv neutralize
odoo-compose-venv neutralize --dry-run
odoo-compose-venv neutralize --dry-run ./preview-sqlOdoo versions: For Odoo 16.0+, the command uses Odoo's built-in neutralization and installed modules' neutralize.sql scripts to disable or redirect supported production-side effects (including scheduled actions, outgoing email and external integrations). For Odoo 13.0–15.0, it uses compatibility neutralization of scheduled actions and outgoing/incoming mail servers: Docker uses the Odoo ORM and Venv uses PostgreSQL commands.
After standard neutralization, the command executes optional *.sql files directly inside ROOT/neutralize/, sorted by filename (no recursion):
neutralize/
├── 01-delete-fetchmail-servers.sql
├── 02-disable-outgoing-mail-servers.sql
└── 03-set-ir-config-params.sql
Execution stops at the first SQL error; if the directory is missing, only standard/legacy neutralization runs. Custom SQL accepts ${section:option} references to the current project INI, including nested [vars] references. Each reference is inserted as a quoted PostgreSQL text literal—do not surround it with SQL quotes.
odoo-project.ini:
[vars]
base_url = https://staging.example.comneutralize/03-set-ir-config-params.sql:
INSERT INTO ir_config_parameter (key, value)
VALUES ('web.base.url', ${vars:base_url})
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value;Use \${section:option} to include the reference literally. Paired backslashes before ${...} produce literal backslashes; $ and $$ are unchanged. Unknown references cause an error before the affected SQL file is sent to PostgreSQL. Resolved SQL is streamed to psql without being saved, and original SQL files are not modified. See examples/neutralize/ for templates.
Without an explicit directory, preview files are written to ROOT/.odt-env/neutralize-preview/. Only custom ROOT/neutralize/*.sql is previewed, not Odoo's built-in neutralization. No SQL is executed.
Security note: Preview files may contain interpolated passwords or tokens. Protect or delete them after inspection. On Unix-like systems, newly created preview SQL files have permissions
0600.
Neutralization is a separate step after restoring the database and filestore:
# Docker workflow:
docker compose stop odoo
odoo-compose restore-db --copy ./odoo-backups/production.dump
odoo-compose restore-filestore ./odoo-backups/production-filestore.tar.gz
odoo-compose update --all
odoo-compose neutralize
docker compose up -d odoo
# Native workflow:
odoo-compose-venv restore-db --copy ./odoo-backups/production.dump
odoo-compose-venv restore-filestore ./odoo-backups/production-filestore.tar.gz
odoo-compose-venv update --all
odoo-compose-venv neutralizeThe Docker example uses docker compose stop odoo and docker compose up -d odoo intentionally: only the Odoo service is stopped and restarted, while PostgreSQL remains available for the restore operations.
Generate shell-completion definitions for either CLI. See Shell completion under Installation for activation instructions.
Usage
{odoo-compose|odoo-compose-venv} completion {bash|zsh|fish|powershell}
Arguments
bash,zsh,fish,powershellShell format to generate.
The odoo-compose project file is an INI file that describes the Odoo workspace to create.
At minimum, the project file must contain this section:
[odoo]
The following sections are supported:
[vars]— optional reusable variables for INI interpolation[virtualenv]— optional Python and dependency settings[odoo]— required Odoo source settings[addons.<name>]— optional addon sources[docker]— optional Docker workflow/deploy generation settings[docker.share.<name>]— optional host-directory shares mounted into the Odoo container[config]— optional Odoo server configuration values
- The project file can have any filename when passed explicitly as an existing file. When no explicit INI is provided,
setupusesROOT/odoo-project.inior creates it from the default template if missing. Remote INI sources and merged include layers are materialized asROOT/odoo-project.ini. - INI interpolation is supported, so values such as
${odoo:version}can be reused across sections. - Use
\${section:key}for a literal${section:key}. - Multiple INI layers can be composed with
-i/--include. Later layers override earlier layers; multi-line values are replaced as whole option values, not appended. - The optional
[vars]section is useful for reusable values referenced as${vars:name}. - Values from
[vars]can be overridden or injected from the CLI with-e name=value/--extra-var name=value. - Supported project options can be set or overridden directly with
-S section:key=value/--set section:key=value, even when the section or option is omitted from the INI file. Structured sections accept only their documented keys. - Multi-line values are used for lists such as
requirements,constraints,build_constraints, andrequirements_ignore.
This section is optional.
Use it for reusable values that you want to interpolate in other sections.
A major advantage of [vars] is that its values can also be overridden directly from the CLI with -e KEY=VALUE / --extra-var KEY=VALUE. This makes it easy to keep a single project file and adjust things like Odoo version, branch, commit, or database name per run without editing the file.
Example:
[vars]
branch = 18.0
db = odoo
[odoo]
version = 18.0
branch = ${vars:branch}
[config]
db_name = ${vars:db}
db_user = odoo
db_password = odooCLI override example:
odoo-compose-venv setup odoo-project.ini --sync-all --create-venv -e branch=dev -e db=odoo_devThis section is optional.
python_version— Python version for the virtual environment. If omitted,odoo-composechooses a default version based on the selected Odoo version.managed_python— whetheruvshould install and manage Python automatically. Default:true.requirements— additional Python requirements to install. Multi-line list.constraints— dependency constraints used during resolution without installing packages by themselves. Explicitrequirementstake priority over matching constraints. Multi-line list.build_constraints— additional build constraints used during dependency compilation. Multi-line list.requirements_ignore— package names to ignore when collecting requirements from addon repositories. Multi-line list.
Example:
[virtualenv]
managed_python = false
python_version = 3.11
requirements =
lxml>=6
psycopg2-binary==2.9.9
constraints =
urllib3<2
requirements_ignore =
psycopg2This section is required.
version— Odoo version inX.0format, for example18.0. Required.path— local Odoo source directory. Relative paths are resolved relative toROOT/.repo— Git repository URL for Odoo. Default: the official Odoo repository.branch— Git branch to check out. Default: the same value asversion.commit— optional Git commit to check out after fetching the selected branch. When set, the repository is pinned to that exact revision.shallow— whether to use a shallow clone. Default:true. Ignored whencommitis set.
Odoo must use exactly one of these source modes:
- local Odoo source:
version+path - Git-managed Odoo source:
version+ optionalrepo,branch,commit, andshallow
Git-managed example:
[odoo]
version = 18.0
repo = https://github.com/odoo/odoo.git
branch = 18.0
commit = e6ec487
shallow = trueLocal source example:
[odoo]
version = 18.0
path = ../odooAddon sections are optional. You can define as many as needed.
Each addon must use exactly one of these source types:
- local addon path:
path - Git repository:
repo+branch(+ optionalcommitandshallow)
Rules:
- For a local addon, use only
path. - For a Git addon,
repoandbranchare required. commitis optional for a Git addon. When set, the repository is pinned to that exact revision.shallowis optional for Git addons and defaults totrue. It is ignored whencommitis set.- Relative local paths are resolved relative to
ROOT/. - Git-based addons are cloned into
ROOT/odoo-addons/<name>/. - All configured addon directories are automatically appended to the generated
addons_path.
Examples:
[addons.my-custom-addons]
path = odoo-addons/my-custom-addons
[addons.oca-web]
repo = https://github.com/OCA/web.git
branch = ${odoo:version}
commit = abcdef1This section is optional.
restart— restart policy for Odoo, PostgreSQL and optional Mailpit (no,always,unless-stopped, oron-failure). Default:unless-stopped.base_image— Docker image used as the base image in generated Dockerfiles. Default:odoo:${odoo:version}.odoo_source— selects the Odoo source for Docker.imageuses Odoo frombase_image;workspaceuses the workspace Odoo source. Default:image.postgres_version— PostgreSQL major version used by the local Docker Compose database service. Default:16.restic_version— version ofrestic/resticin generated local and deploy Dockerfiles. Default:0.19.1. An explicitly set value must be a concrete version; empty values andlatestare invalid.mailpit_enabled— enables a persistent Mailpit service in the local Docker workflow for SMTP and POP3 mail testing. Default:false. It does not affect the deploy build context.mailpit_version— version ofghcr.io/axllent/mailpitin local Compose. Default:v1.31.3. An explicitly set value must be a concrete version; empty values andlatestare invalid.mailpit_webui_port— host port used for the Mailpit web UI. Default:8025. The internal Mailpit web UI port remains8025.odoo_cpus,db_cpus,mailpit_cpus— optional CPU limits for the respective Compose services; positive decimal CPU counts such as0.5or2.0.odoo_mem_limit,db_mem_limit,mailpit_mem_limit— optional memory limits for the respective Compose services; positive amounts such as256mor4g.
Example:
[docker]
restart = no
postgres_version = 18
restic_version = 0.19.1
mailpit_enabled = true
mailpit_version = v1.31.3
mailpit_webui_port = 18025Each section defines one read-write host-directory bind mount for the generated Odoo Compose service.
source— required host directory. Relative paths are resolved relative toROOT/; absolute paths are preserved.target— required absolute path inside the Odoo container.
The generated mount always sets bind.create_host_path: false. The source directory must therefore be created or mounted, and its permissions prepared, by host provisioning or the deployment process before Docker starts. odoo-compose does not create, chmod, or chown share sources.
Example:
[docker.share.exchange]
source = /mnt/nfs/exchange
target = /mnt/exchangeThe same share can be configured through the CLI:
odoo-compose setup \
--set docker.share.exchange:source=/mnt/nfs/exchange \
--set docker.share.exchange:target=/mnt/exchangeThis section is optional.
When present, it contains Odoo server configuration values written into ROOT/odoo-configs/odoo-server.conf.
For odoo-compose-venv, omitting [config] still generates a valid config file with generated values such as addons_path and data_dir. The Docker workflow does not generate odoo-server.conf.
You can define standard Odoo configuration options here.
Special rules:
addons_pathmust not be set in[config].odoo-composealways generates it automatically.data_dirmay be set in[config]. If provided, it overrides the default data directory location.
Example:
[config]
db_host = 127.0.0.1
db_port = 5432
db_name = odoo
db_user = odoo
db_password = odoo
http_port = 8069