Skip to main content

Command Reference

All commands are run as odoo <command>. Use odoo --help or odoo <command> --help for up-to-date option lists.

Global flags​

odoo -f <command> # force (skips "are you sure?" prompts)
odoo -v <command> # verbose output
odoo -p <name> # override project name

Container Lifecycle​

odoo up [-d] [machines...]​

Start containers. -d runs in background (daemon mode).

odoo up -d # start all containers in background
odoo up -d odoo # start only the odoo container

odoo down [-v] [--postgres-volume]​

Stop and remove containers. Requires -f on production systems.

odoo down # stop containers, keep volumes
odoo -f down -v # also remove all volumes (destroys database!)

odoo stop [machines...]​

Stop containers without removing them.

odoo restart [machines...]​

Restart containers. Every run is recorded in the command log.

odoo build [machines...]​

Build Docker images locally. Required after Dockerfile changes.

odoo kill​

Force-kill all containers.

odoo rm​

Remove stopped containers.

odoo recreate [machines...]​

Recreate containers without rebuilding images.

odoo dev [-b/--build] [-k/--kill]​

Start containers in dev mode: combines build + up + watch, so code changes are picked up live. -b forces a rebuild first, -k kills existing containers before starting.


Configuration​

odoo reload​

Regenerate docker-compose.yml from current settings. Run this after every settings change before odoo up.

Also writes .vscode/launch.json and .vscode/tasks.json for the project and installs/updates the Zebroo VS Code extension (if the code CLI is available) — no separate setup command is needed for VS Code integration.

If the odoo image hook (images/odoo/__after_compose.py) fails, odoo reload prints the traceback and exits with a non-zero code. That hook computes the project requirements and the odoo configuration; an image built from a compose file without them would miss the project's pip packages. Hooks of the other services (proxy, cronjobs, …) only print a warning with the traceback.

odoo setting <KEY>=<VALUE>​

Set a project setting. Writes to ./.odoo/settings and triggers reload.

odoo setting DEVMODE=1
odoo setting PROXY_PORT=18069
odoo setting ODOO_PYTHON_VERSION=3.12
odoo setting HUB_URL=registry.example.com:443/myproject

Flags:

  • -u / --user-wide: write to ~/.odoo/settings
  • -s / --system-wide: write to /etc/odoo/settings
  • --no-reload: skip auto-reload after setting

odoo setup next-port​

Find and assign the next free port for PROXY_PORT, DEBUG_PORT, and (on macOS) HOST_DB_PORT.

odoo status​

Show project name, Odoo version, database connection URL, and key config values. (Also reachable as odoo setup status; odoo status is the direct, unambiguous form.)

Passwords (dashboard password, DEFAULT_DEV_PASSWORD) are masked, because the same output closes odoo update and odoo up -d and ends up in CI logs and terminal transcripts. odoo status --show-secrets prints them.

odoo setup remove-web-assets​

Fix broken CSS/JS. Clears web assets from database; they are regenerated on next admin login.

odoo setup setup-pyenv​

Set up a local pyenv-managed Python environment for the robot/test tooling, so tests show up correctly in VS Code.

odoo config [-f/--full]​

Print the effective configuration for the current project. --full shows the full environment instead of the shortened default.

odoo upgrade​

Upgrade zodoo to the latest version (pulls ~/.odoo/images and reinstalls zodoo).


Database​

odoo -f db reset​

Drop and reinitialize the database. Destructive — requires -f.

odoo db pgactivity​

Show live PostgreSQL activity (like htop for the DB).

odoo db pgcli [--dbname <name>]​

Enhanced interactive PostgreSQL CLI with autocomplete.

odoo db psql [--sql <query>]​

Standard psql CLI. Can run SQL non-interactively:

odoo db psql --sql "SELECT COUNT(*) FROM res_partner;"

odoo db drop-db​

Drop the current database.

odoo db anonymize​

Anonymize sensitive data (emails, phone numbers, etc.) in the database.

odoo db show-table-sizes [--top N]​

Show the largest tables in the database.

odoo db dbcompare <file1> <file2>​

Compare two database dumps.

odoo restore-web-icons​

Repairs broken ir.attachment links after a database restore, by deleting and recreating the affected web-icon attachments. (Also reachable as odoo talk restore-web-icons.)


Backup & Restore​

odoo backup odoo-db [path]​

Backup the database. Uses a default filename if no path is given.

odoo backup odoo-db # default name
odoo backup odoo-db /backups/mydb.zip # custom path

odoo backup files​

Backup the Odoo filestore.

odoo backup all [filename]​

Backup database + filestore in one archive.

odoo restore odoo-db [path]​

Restore the database. Shows an interactive file picker if no path is given.

With DEVMODE=1 the restored database is turned into a dev copy afterwards (skip with --no-dev-scripts). Without DEVMODE, --neutralize runs only Odoo's own neutralization, e.g. for a staging copy that keeps its passwords.

odoo restore odoo-db # interactive picker
odoo -f restore odoo-db /backups/mydb.zip
odoo -f restore odoo-db --neutralize mydb.zip # staging, DEVMODE=0

A DEVMODE restore runs these steps on the restored database, before it gets its final name - a copy of a production database is never reachable un-neutralized:

  1. Odoo's official neutralization (Odoo 16 and newer): zodoo runs odoo-bin neutralize -d <db> in a one-off odoo container. Odoo executes the data/neutralize.sql of every installed module - the same mechanism as the database manager's "neutralize" - and sets database.is_neutralized, so the web client shows the neutralization banner. If it fails, the restore aborts.
  2. zodoo's turndb2dev.sql: only what Odoo does not cover (see table).
  3. Project scripts: SQL files listed under neutralize in the MANIFEST and devscripts/turn-into-dev.sql.
  4. Passwords of all users are reset to DEFAULT_DEV_PASSWORD, web.base.url points to the local proxy.
WhatDone by
Cronjobs off (except autovacuum)Odoo (base)
Mail servers off, dummy server addedOdoo (base); zodoo points it to the local mail catcher
Incoming mail (fetchmail) offOdoo (mail); zodoo points it to the local mail catcher
Mail and fetchmail servers renamedzodoo: Test-Mailserver (<old name>), so a dev copy is recognizable at a glance
Webhooks of server actions cutOdoo (base)
Payment providers disabled, Stripe keys removedOdoo (payment, payment_stripe, ...)
IAP tokens disabled (+disabled)Odoo (iap)
Web push keys and devices removedOdoo (mail)
Google/Outlook calendar tokens, sync stoppedOdoo (google_calendar, microsoft_calendar); zodoo additionally deletes the Microsoft client secrets (19)
zSYNC: runs paused, channel host local, hooks offzSYNC's own neutralize.sql (zbsync, zbsync_trigger_methods)
Two-factor login (TOTP) removedzodoo
New database.uuidzodoo - Odoo only does this when a database is duplicated or restored as a copy, not on neutralize
database.enterprise_code removedzodoo
Passwords reset, web.base.url localzodoo

--neutralize without DEVMODE only runs step 1: TOTP, database.uuid and passwords stay as they are.

Before Odoo 16 there is no odoo-bin neutralize; there turndb2dev.sql disables the cronjobs itself.

Existing projects need odoo reload && odoo build odoo once, so the odoo image contains /odoolib/neutralize.py; otherwise the restore aborts with that hint.

Run the steps again on an existing database with odoo dev-env turn-into-dev, or only Odoo's neutralization with odoo dev-env neutralize (requires DEVMODE=1 or -f).

odoo restore files​

Restore the Odoo filestore.

odoo restore list​

List available backup files.


Shared Filestore​

On hosts carrying several instances of the same dump, ODOO_FILES_COMMON=1 keeps one pool of attachment files in <filestore>/_common. Content is shared via hardlinks; sharing the directory via a symlink instead destroys attachments, because Odoo's garbage collection bookkeeping (checklist) must stay private per database. See 17-filestore.md for the concept, the failure mode and how to repair a damaged instance.

odoo filestore sync [--pull] [--no-heal] [--no-dedup] [--wait] [--dry-run]​

The everyday command, for the current project: optionally mirror missing files from FILESTORE_UPSTREAM into the pool (--pull), then link back what the database references but the instance is missing, then dedup into the pool.

Strictly additive and idempotent — it never moves, replaces or rebuilds a directory an instance is serving from, so nobody sees a missing filestore. An flock keeps two runs from overlapping; the lock is held by the file descriptor, so a killed run cannot leave a blocking lock behind. Interactively the lock does not wait - it says so and stops. --wait queues instead, which is what the instance cronjob (CRONJOB_FILESTORE_HEAL) uses: all instances of a host share one pool, so skipping would mean most of them never run.

--pull is off by default on purpose: it talks to another machine, and on a dev host carrying instances of many different production systems you rarely want that unattended. FILESTORE_UPSTREAM is therefore a per-project setting (FILESTORE_UPSTREAM_BWLIMIT throttles the transfer).

odoo filestore install-cron [--at HH:MM] [--remove]​

Install a nightly filestore dedup in the user's crontab, ioniced. The pool belongs to the filestore root rather than to a project, so one entry per root is enough. Healing is not part of it — that needs each instance's database and therefore belongs to the instance or to the CI system after a restore.

odoo filestore dedup​

Hardlink per-database filestores into the shared _common pool. Skips directories that are still symlinks and asks you to run unshare first.

odoo filestore unshare [-a/--all]​

Replace legacy <db> -> _common symlinks by real directories of hardlinks, so each database gets its own GC checklist again. Uses no additional disk space. Database-driven: only files referenced by ir_attachment are materialised; an unreachable database is left untouched rather than emptied.

--all covers every symlinked database served by this project's postgres. Where each instance runs its own postgres container, run it once per project.


Encrypted Offsite Backup​

Pushes the pgBackRest repository and this database's filestore to a remote repository using restic. The usual target is our own backup server (restic-backup), which runs rest-server in append-only mode: this machine may write but cannot delete anything, so a compromised Odoo host cannot destroy the history. A Hetzner Storage Box (sftp:) or a mounted filesystem work too.

Encryption happens on this machine before anything leaves it: the storage provider only ever sees ciphertext and cannot read or silently alter the backup.

For our backup server, do not wire this up by hand — run odoo offsite register (see below). Otherwise set RUN_OFFSITE=1 plus OFFSITE_REPO and OFFSITE_PASSPHRASE, then odoo reload && odoo build offsite. See offsite/default.settings for the retention, compression and bandwidth knobs.

Keep the passphrase somewhere other than this machine. Without it the backup cannot be opened — and it is needed precisely when the machine is gone.

A run aborts loudly when no database state would end up in the snapshot — neither via pgBackRest nor via a dump. A snapshot of attachments alone looks like a backup until someone needs to restore. OFFSITE_ALLOW_WITHOUT_DB=1 switches that check off, and should only be used when the database is provably backed up elsewhere.

odoo offsite register​

Request a customer area on the backup server. The first call files the request; an admin approves it in the server's admin page, and the same call then picks up credentials plus the server certificate and writes them into the settings. The repo key is shown to the admin once — it goes into 1Password there, and the backup server does not keep it.

odoo offsite backup​

Run a backup now. The same command runs nightly via OFFSITE_BACKUP_CRON (default 04:00, after the pgBackRest backup) and is a quiet no-op on projects without RUN_OFFSITE=1.

It runs whichever streams are configured — filestore, database, or both. With RUN_OFFSITE=1 but no target at all it fails loudly instead of returning success; so does a filestore-only target when the database is covered by neither pgBackRest nor OFFSITE_WO_DB_RECIPIENT.

odoo offsite list / odoo offsite info​

List the archives in the repository / show repository stats (size, deduplication).

odoo offsite check​

Verify integrity by re-reading the data. Takes time and costs traffic.

odoo offsite prune​

Apply the retention rules now (OFFSITE_KEEP_DAILY / _WEEKLY / _MONTHLY). Against an append-only target this is refused with an explanation: retention has to run on the backup server, and it must actually run there — otherwise the repository grows without bound.

odoo offsite restic <args...>​

Escape hatch: run an arbitrary restic command against the repository, e.g. odoo offsite restic snapshots --compact.


Module Management​

odoo update [module...]​

Update installed modules. Without arguments, updates all modules listed in MANIFEST install.

odoo update # update all
odoo update sale account # update specific modules

The output of the last run is in ./update.log. Every run is also recorded in the command log.

Command log (.odoo-commands.log)​

odoo update and odoo restart append one JSON line per run to .odoo-commands.log in the project directory - on the host as well as inside the container (UPDATE_ON_STARTUP), since both see the same directory. Unlike update.log the file is never emptied, so it answers "when was this instance last updated or restarted, with which arguments, and did it work?".

{"ts": "2026-10-05T15:32:32+02:00", "command": "update", "argv": ["update", "sale"], "project": "myproj", "in_container": false, "nested": false, "result": "ok", "exit_code": 0, "error_type": null, "duration_s": 84.3}
  • result is ok, error or aborted (Ctrl+C); exit_code is the value the shell sees ($?), e.g. 255 for sys.exit(-1). On errors only the exception type is stored, not its message.
  • nested is true when another command triggered the run internally; argv then shows the command you typed.
  • Not recorded: environment, settings, user or host names. Option values whose name looks like a secret (--...password=, --token ...) and credentials in URLs are written as ***.
  • A call rejected by argument parsing (e.g. an unknown option) never started and is not recorded. If the file cannot be written, the command prints a warning and carries on.

The file is runtime data of your checkout. zodoo adds /.odoo-commands.log to the project .gitignore before it writes the first line; new projects get the rule (and /update.log) from the template. Show the last runs with:

tail -n 5 .odoo-commands.log

odoo module uninstall <module...>​

Uninstall modules.

odoo module show-install-state​

Show which modules from MANIFEST are installed/uninstalled.

odoo module update-i18n​

Update translations for installed modules.


Debugging​

odoo debug odoo_debug [--port <port>]​

Start a debug container. Inside the container prompt, type debug and press ENTER.

Then:

  • Navigate to https://<host>/debugpython → activates debug mode for your browser session (sets cookie)
  • Your Python requests go to the debug container; websocket and other requests go to the normal container
  • Reset with https://<host>/debugpython_off

See Debug Mode Guide for full details.

odoo shell​

Open an interactive Odoo Python shell inside the running container.

Written as odoo-shell here until 06.09.2026. That is the name the command carries in zodoo's internal registry (Commands.register(shell, "odoo-shell")), not the one the CLI answers to — typing odoo odoo-shell gets you a usage error.

# Example usage inside shell:
env['res.partner'].search([]).mapped('name')

Docker Registry​

odoo docker-registry login​

Authenticate with the configured Docker registry (HUB_URL).

odoo build​

Build all images (required before regpush).

odoo regpush​

Push all images (including base images like postgres, redis) to the registry. Images are tagged with a SHA-based name.

odoo regpull​

Pull all images from the registry. Requires REGISTRY=1 and HUB_URL to be configured.

See Using the Registry for full details.


Source / Project Management​

odoo src init <path> [version]​

Initialize a new Odoo project at <path> (also available as odoo init).

odoo src find-duplicate-modules​

Find modules with duplicate names across addon paths.

odoo src apply-gimera-if-required​

Apply pending gimera updates if needed.


Robot Framework​

See Robot Tests for the full workflow.

odoo robot setup​

Wire Robot Framework into the project: adds odoo-robot_utils to gimera.yml, robot_utils to the MANIFEST install list and addons_robot to addons_paths, creates the ~/.robotenv virtualenv, and runs gimera apply. Idempotent.

odoo robot new <name>​

Create tests/<name>.robot from the template. -I / --no-install-pip skips the setup step.

odoo robot run [file]​

Run Robot Framework tests. Requires devmode. Notable options: --all, --user, --tags, --parallel, --repeat, --test-tv (watch the browser at /test.tv/) and --debug.

odoo robot list​

List the project's robot suites - the same selection odoo robot run-all uses, so with robotests in the MANIFEST only the matching files. zCICD reads the same list through odoo module list-robot-test-files.

odoo robot run-all​

Run the project's robot suites one after the other (requires DEVMODE=1). If the MANIFEST has robotests, only the files matching those glob patterns (relative to the project root) run; without the key every *.robot file of the project runs, except keywords/, library/ and test_template.robot.

  • --list: print the selected files without running them
  • --filter <text>: additionally keep only files whose name contains the text
  • --retry, --timeout: retries per file and wait timeout

odoo robot run <file> still runs any single file, whether it matches robotests or not. odoo robot list and odoo module list-robot-test-files report the same selection as run-all.

odoo robot make-variable-file​

Generate the .robot-vars variables file.

odoo robot cleanup​

Clean up after test runs.

odoo robot start-cobot​

Start cobot, reachable at http://<host>/cobot.

In test files, use comments to declare module requirements:

#odoo-require: crm,sale_stock
#odoo-uninstall: partner_autocomplete

Performance​

odoo benchmark fields <model>​

Benchmark every field of a model to find slow computed fields under real database conditions.

odoo benchmark curl​

Same idea, scoped to the fields a specific slow request actually asked for — paste a web_search_read cURL command copied from Chrome DevTools.

See Benchmarking for full option lists and a worked example.


Snapshots (btrfs/zfs only)​

Fast database snapshots using filesystem-level copy-on-write:

odoo snapshots create <name> # create snapshot
odoo snapshots restore <name> # restore snapshot
odoo snapshots list # list snapshots
odoo snapshots delete <name> # delete snapshot

Requires btrfs or zfs filesystem for the postgres data volume.

On zfs the dataset is created with xattr=sa acltype=posixacl. The linux default xattr=on stores every extended attribute as a hidden directory with one file per attribute, which costs additional IOs on every access. If you prepare the dataset yourself, set those two properties as well - they only apply to newly written attributes, existing files keep the old layout.