Odoo Docker environment variables control how the Odoo container finds PostgreSQL. Docker Compose's .env file keeps passwords and versions out of your compose file. This guide covers the variables the official Odoo image reads, how .env and --env-file work, which value wins when several sources disagree, where odoo.conf fits in, and how to pass the database password as a Docker secret instead of a plain variable.
Environment variables the Odoo image reads
The official odoo image's entrypoint reads only a handful of variables, all about the database connection:
| Variable | Default | Meaning |
|---|---|---|
HOST | db | PostgreSQL host (the service name in Compose) |
PORT | 5432 | PostgreSQL port |
USER | odoo | PostgreSQL role Odoo connects as |
PASSWORD | odoo | Password for that role |
PASSWORD_FILE | (unset) | Path to a file holding the password; used instead of PASSWORD |
Everything else, such as workers, memory limits, proxy_mode or list_db, is not read from the environment. Those settings belong in odoo.conf or on the command line. A variable such as WORKERS=4 does nothing.
odoo.conf in Docker, and which value wins
The image reads /etc/odoo/odoo.conf. Mount your own copy to change server settings:
services:
web:
image: odoo:19.0
environment:
HOST: db
USER: odoo
PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
volumes:
- ./config:/etc/odooOne rule surprises people: if odoo.conf contains db_host, db_port, db_user or db_password, the entrypoint uses the file's value and ignores the matching variable. Leave those four keys out of odoo.conf and let the environment supply them. Otherwise a password change in .env silently has no effect.
For a single setting you can also pass an Odoo command-line flag. The entrypoint adds the database arguments for any command that starts with a dash:
services:
web:
command: ["--db-filter=^mydb$$"]
Note the $$: Compose treats $ as the start of a variable, and $$ produces a literal dollar sign.
How the .env file works
Compose automatically reads a file named .env in the project folder and uses it to fill ${...} placeholders in docker-compose.yml:
# Odoo stack settings
ODOO_VERSION=19.0
POSTGRES_PASSWORD=replace-with-a-strong-password
ODOO_PORT=8069services:
web:
image: odoo:${ODOO_VERSION:-19.0}
ports:
- "127.0.0.1:${ODOO_PORT:-8069}:8069"Important points:
.envvalues are for substitution in the compose file. They do not reach the container unless you reference them underenvironment:or load the file withenv_file:.- Lines starting with
#are comments. Unquoted and double-quoted values are interpolated; single-quoted values are taken literally. Use single quotes for passwords containing$. - Keep
.envout of Git and commit a.env.examplewith placeholder values instead.
Substitution syntax
| Syntax | Result |
|---|---|
${VAR} | Value of VAR |
${VAR:-default} | default if VAR is unset or empty |
${VAR-default} | default only if VAR is unset |
${VAR:?message} | Stop with message if VAR is unset or empty |
${VAR:+other} | other if VAR is set and not empty, otherwise empty |
Use :? for every password. Without it, a missing .env starts PostgreSQL with an empty value instead of failing.
One file per environment with --env-file
docker compose --env-file .env.staging up -d
docker compose --env-file .env.common --env-file .env.production up -d
Passing --env-file replaces the default .env; it is not added on top of it. With several --env-file flags, later files override earlier ones. Running staging and production from one folder also needs different project names (-p odoo-staging) so their containers and volumes do not collide. Separate environments are covered in Odoo Docker staging.
Precedence: which value the container gets
When a variable is set in several places, Docker Compose uses this order, highest first:
docker compose run -e VAR=valueon the command line.environment:orenv_file:entries whose value is interpolated from your shell or an env file, such asPASSWORD: ${POSTGRES_PASSWORD}.- Literal values under
environment:. - Values from an
env_file:attribute. ENVlines in the image.
For ${...} substitution, a variable exported in your shell beats the same variable in .env. A literal value under environment: (for example USER: odoo) is not overridden by your shell. Only interpolated values are. To see the final result after all files and variables are applied, run docker compose config. It prints passwords in clear text, so keep the output private.
Odoo Docker secrets: pass the password as a file
Environment variables show up in docker inspect and in the output of docker compose config. Both the Odoo and PostgreSQL images can read the password from a file instead, which Compose mounts at /run/secrets/<name>:
services:
db:
image: postgres:16
environment:
POSTGRES_DB: postgres
POSTGRES_USER: odoo
POSTGRES_PASSWORD_FILE: /run/secrets/postgresql_password
secrets:
- postgresql_password
web:
image: odoo:19.0
environment:
HOST: db
USER: odoo
PASSWORD_FILE: /run/secrets/postgresql_password
secrets:
- postgresql_password
secrets:
postgresql_password:
file: ./secrets/odoo_pg_passThis mirrors the example in the official Odoo image documentation. Things to know when you run it with plain Docker Compose (no Swarm):
- The secret is a bind mount of your local file. It is not encrypted. It keeps the password out of the compose file, environment listings and
docker inspect, but anyone who can read the file on the host can read the password. - The file keeps its host permissions, so it must be readable by the container's user. Do not commit the
secrets/folder. - A secret can also come from a host variable (
environment: POSTGRES_PASSWORDunder the top-level secret) instead of a file. That form works only with Docker Compose. - The Odoo entrypoint still hands the password to Odoo as a command-line argument, so it is visible in the process list inside the container. The secret protects the compose file and Docker metadata, not the running process.
external: truesecrets created withdocker secret createbelong to Docker Swarm.
The master password (admin_passwd) lives in odoo.conf, not in an environment variable. Treat that file like a secret too.
Checklist
- Database connection in
HOST/USER/PASSWORD(orPASSWORD_FILE), never inodoo.conf. - Server settings in
odoo.conf; see Odoo server configuration for workers and limits. ${VAR:?}on every required value;.envandsecrets/in.gitignore.- A full working stack is in the Odoo Docker Compose guide.
Prefer not to manage this?
On Easy Instance the database connection, passwords and backups are set up for you, and Odoo settings are available in Odoo's own admin panel. Managed Odoo 17 to 20 costs $30 per worker a month, and the 10-day free trial needs no credit card.