Skip to main content

Settings Reference

Settings are stored in plain KEY=VALUE text files. They are merged in this order (later overrides earlier):

  1. ~/.odoo/images/odoo/default.settings (zodoo defaults, don't edit)
  2. /etc/odoo/settings (system-wide)
  3. ~/.odoo/settings (user-wide)
  4. ./.odoo/settings (project-specific, committed to your repo)

Set settings via CLI:

odoo setting <KEY> <VALUE> # project-level (./.odoo/settings)
odoo setting -u <KEY> <VALUE> # user-level (~/.odoo/settings)
odoo setting -s <KEY> <VALUE> # system-level (/etc/odoo/settings)

Or edit the files directly. Run odoo reload after manual edits.


Project Settings​

SettingDefaultDescription
PROJECT_NAME—Container name prefix and postgres volume name. Keep short (max ~20 chars).
DBNAMEPROJECT_NAMEPostgreSQL database name.
PROXY_PORT—HTTP port to access Odoo in browser. Set with odoo setup next-port.
DEBUG_PORT—Port for Python debugger. Set with odoo setup next-port.
EXTERNAL_DOMAINhttp://localhostPublic URL of the system. Comma-separated list of URLs is allowed; odoo status prints each on its own line.

Developer Settings​

SettingDefaultDescription
DEVMODE01 = on restore: neutralize the database with Odoo's odoo-bin neutralize plus zodoo's additions (see odoo restore odoo-db), reset all passwords to DEFAULT_DEV_PASSWORD. Project-specific scripts come from the MANIFEST key neutralize.
DEFAULT_DEV_PASSWORDadminPassword set for all users when DEVMODE=1 and a DB is restored.
ODOO_DEMO01 = load demo data on db reset.
ODOO_ENABLE_DB_MANAGER01 = enable Odoo's built-in database manager at /web/database/manager.

Odoo Server​

SettingDefaultDescription
ODOO_LOG_LEVELemptyOdoo log level: debug, info, warn, error, critical. Empty: warn on real instances, info with DEVMODE=1. warning is accepted and written as warn.
ODOO_DEBUG_LOGLEVELinfoLog level inside the debug container.
ODOO_MAX_CRON_THREADS2Cron threads of the cronjobs role. Web server, queue jobs, update and debug keep the value from their own config file.
ODOO_WORKERS_WEB6Number of Odoo web worker processes.
ODOO_PYTHON_VERSION—Python version for the Odoo container (e.g. 3.12).
ODOO_INSTALL_LIBPOSTAL0No effect. Nothing reads it any more (checked 2026-09-06); libpostal was removed from the Odoo images. It is still written from settings.txt into every project.

Odoo 19 and later: Odoo reads every config-file option also from an environment variable ODOO_<OPTION>, and the environment beats the config file. Some zodoo settings carry such names (ODOO_MAX_CRON_THREADS, ODOO_LOG_LEVEL, ODOO_DATA_DIR, ODOO_DBFILTER). zodoo therefore does not pass a variable to odoo-bin when the option is set in the role's config file; the config file wins, as before Odoo 19. The Odoo log names the variables it held back (Not passing to odoo-bin (set in config_webserver): ...; effective max_cron_threads=0). Options that are not in the config file still come through the environment - e.g. ODOO_LOG_LEVEL keeps setting the log level via the command line of the running roles.

Containers​

SettingDefaultDescription
RUN_ODOO1Run the Odoo container (web + supervised sibling roles).
RUN_ODOO_CRONJOBS1Spawn the cronjobs sibling role inside the odoo container. 0 = no cronjobs at all; the other roles run with max_cron_threads = 0 from their config file.
RUN_PROXY1Run the Node.js reverse proxy.
RUN_PROXY_PUBLISHED01 = expose proxy port to host (required for browser access).
RESTART_CONTAINERS01 = set restart: unless-stopped on all containers.

PostgreSQL​

SettingDefaultDescription
POSTGRES_VERSION14PostgreSQL version: 11, 12, 13, 14, 15.
HOST_DB_PORT—Expose postgres on this host port (macOS/WSL only).
NAMED_ODOO_POSTGRES_VOLUME—Use a named external volume (not deleted with down -v).
DB_SSLMODEdisablePostgreSQL SSL mode.

Queue Jobs​

SettingDefaultDescription
ODOO_QUEUEJOBS_CHANNELSroot:1Queue channel configuration: root:4,magento2:1.
QUEUEJOBS_MAX_AGE_BEFORE_RESTART_MINUTES120Restart queuejob worker if idle for this long.

Standard Odoo Image​

SettingDefaultDescription
ODOO_STANDARD_IMAGE01 = run the official odoo:<version> image from Docker Hub instead of our build.
ODOO_STANDARD_IMAGE_NAME—Override the image reference (default odoo:${ODOO_VERSION_INT}).

Everything around Odoo keeps working: proxy, postgres, pgbackrest and the whole monitoring stack are independent of the Odoo image. What lives inside our image is gone, so these commands are unavailable or behave differently:

  • odoo shell / odoo debug odoo_debug / unit tests / odoo lang — refused with a clear message (they need /odoolib).
  • odoo update and odoo db reset run through the official Odoo CLI (odoo -i <mods> -u <mods> --stop-after-init) in a one-off container.
  • odoo backup files does not see the filestore: it lives in the named volume odoo-standard-data, not under ${ODOO_FILES}. The database is fully covered by pgbackrest.

Rootless Docker​

SettingDefaultDescription
DOCKER_ROOTLESSauto-detect1 = the daemon behind DOCKER_HOST runs rootless. Unset: odoo reload asks docker info and writes the answer.

Under rootless docker, root inside a container is the unprivileged host user; every other container uid is a subuid nobody on the host owns. zodoo therefore runs Odoo as root in the container: OWNER_UID=0 and ODOO_SUDO_CMD=0 are forced, the entrypoint renames nothing. On the host, files keep belonging to the user who runs the CLI.

The cronjobs container drives pgBackRest, offsite and the restart watchdog through the docker socket. It gets DOCKER_SOCKET and DOCKER_DATA_ROOT (defaults /var/run/docker.sock, /var/lib/docker); under rootless docker odoo reload replaces the defaults with the user's own daemon (DOCKER_HOST or $XDG_RUNTIME_DIR/docker.sock, and docker info → DockerRootDir). Values set by hand are kept.

Use case: several instances on one machine, each under its own unix user with its own rootless daemon - one instance's container cannot see another's files. Odoo logs a warning about running as root; that is expected here.

Registry​

SettingDefaultDescription
HUB_URL—Docker registry: host:port/path or user:password@host:port/path.
REGISTRY01 = rewrite all image URLs to HUB_URL, disable local builds. Use on production.
DOCKER_IMAGE_TAG—Tag for registry images (e.g. latest, v1.2.3).

Memory Limits​

SettingDefaultDescription
LIMIT_MEMORY_HARD_WEB18 GBHard memory limit for web worker (bytes).
LIMIT_MEMORY_SOFT_WEB16 GBSoft memory limit for web worker.
LIMIT_MEMORY_HARD_QUEUEJOBS18 GBHard memory limit for queuejob worker.
LIMIT_MEMORY_HARD_CRON18 GBHard memory limit for cron worker.
LIMIT_MEMORY_HARD_UPDATE88 GBMemory limit during module updates.

Those are Odoo's own limits and are only enforced in prefork mode, where the master watches its workers. The cron and queuejob roles run with workers = 0, so nothing enforces them there. The settings below are Docker limits on the container itself and work regardless — when the limit is hit, that container is killed and restarted instead of the kernel OOM killer picking a victim on the whole host.

SettingDefaultDescription
MEM_LIMIT_ODOO—Container memory limit for the odoo container(s), e.g. 16g.
MEM_LIMIT_ODOO_WEB—Overrides MEM_LIMIT_ODOO for the web role.
MEM_LIMIT_ODOO_CRON—Overrides MEM_LIMIT_ODOO for the cron role (v11/v13).
MEM_LIMIT_ODOO_QUEUEJOBS—Overrides MEM_LIMIT_ODOO for the queuejob role (v11/v13).

Unset = unlimited (the default). odoo_update and odoo_debug are never limited — updates and migrations are expected to be memory hungry.

APT Proxy (build acceleration)​

SettingDescription
APT_PROXY_IPIP of apt-cacher-ng proxy, speeds up Docker builds by caching apt packages.
RUN_APT_CACHER1 = run the built-in apt-cacher container.

Warmup (startup performance)​

SettingDefaultDescription
MAX_WARMUP_WORKERSODOO_WORKERS_WEBOverride warmup worker count.
MAX_PARALLEL_WARMUP4Max concurrent warmup HTTP requests.
ODOO_READY_TIMEOUT_S60Seconds to wait for Odoo to become reachable.
ODOO_WARMUP_REQUESTSworker countTotal warmup requests to send.

Extra Odoo config​

Append arbitrary Odoo server config via settings:

# Add to [options] section of odoo.conf:
EXTRA_CONFIG_my_key=value

# Or edit ~/.odoo/settings/odoo.config (or odoo.config.<PROJECT_NAME>):
[options]
setting1=value1

[queue_job]
channels=root:4

Encrypted Offsite Backup (restic)​

Full guide: 11-offsite-backup.md. Against our own backup server, do not set these by hand — odoo offsite register fills them in.

SettingDefaultDescription
RUN_OFFSITE0Enables the offsite service. On DEVMODE machines it stays off unless OFFSITE_FORCE_IN_DEVMODE=1.
OFFSITE_REPO(empty)Target: rest:https://host:8000/<area>/ (our backup server), sftp:user@host:port/path, or a path on a mounted filesystem. Empty means "not configured" — the backup command then explains itself instead of doing nothing.
OFFSITE_REST_USER(empty)Area account on the backup server (rest: targets). Governs who may write, not who may read.
OFFSITE_REST_PASSWORD(empty)Password for that account.
OFFSITE_PASSPHRASE(empty)Repository key. Without it the backups are lost — it must exist outside this machine (1Password / the hosting backend).
OFFSITE_ENROLL_URLhttps://10.222.0.106:8443Enrollment service that odoo offsite register talks to.
OFFSITE_LOCAL_DIR(empty)Host directory mounted into the container — only for path targets, so OFFSITE_REPO reads the same on both sides.
OFFSITE_BACKUP_CRON0 4 * * *Nightly run, deliberately after the pgBackRest backup (02:00).
OFFSITE_KEEP_DAILY7Retention. Against append-only targets these describe what should apply server-side; the client cannot prune there.
OFFSITE_KEEP_WEEKLY4
OFFSITE_KEEP_MONTHLY6
OFFSITE_COMPRESSIONautoauto, off (already-compressed sources) or max (line costs more than CPU).
OFFSITE_INCLUDE_DUMPS0Also back up all of $DUMPS_PATH. Off by default: with pgBackRest the database is already covered, and the dumps folder often holds many old states.
OFFSITE_ALLOW_WITHOUT_DB0Emergency exit for the completeness check. The run aborts when no database state would be in the snapshot — a snapshot of attachments alone looks like a backup until someone restores. Only set this when the database is provably backed up elsewhere.
OFFSITE_ALLOW_WITHOUT_FILES0The same check in the other direction: the run aborts when the filestore is missing or empty — which is exactly what an unmounted volume looks like. The database would be saved, the attachments not, and it would only show up on restore.
OFFSITE_LAYOUTsplitsplit writes two repositories per area, <area>/db and <area>/files, so each part has its own visible age on the backup server. flat is the old single-repository behaviour and exists for legacy installations only.
OFFSITE_WO_URL(empty)Write-only receiver for the filestore. Set together with OFFSITE_WO_RECIPIENT it replaces the restic files stream: the machine can then neither read nor delete what it uploaded.
OFFSITE_WO_RECIPIENT(empty)age public key for the write-only path. The private key belongs in 1Password — without it the filestore cannot be restored, with it on the machine the point is lost.
OFFSITE_WO_DB_RECIPIENT(empty)age public key for the write-only database stream (base backups + WAL). Set together with OFFSITE_WO_URL it replaces the restic db stream. A different key from the filestore one on purpose.
OFFSITE_WAL_CRON* * * * *How often WAL segments are pushed. Every minute, so a machine loss costs a minute of transactions rather than a night. Quiet no-op when there is nothing new.
OFFSITE_UPLOAD_LIMIT0Upload brake in KiB/s, 0 = unlimited.
OFFSITE_FORCE_IN_DEVMODE0Run offsite backups on a DEVMODE machine anyway (testing).

Update strategy​

SettingValueDescription
UPDATE_STRATEGY(empty)Update all modules listed in MANIFEST install.
UPDATE_STRATEGYodoo.shOnly update modules changed since last deployment (by version).