Odoo Docker Environment Variables, .env and Secrets

How Odoo Docker environment variables, .env files and --env-file work, which value wins over odoo.conf, and how to pass the database password as a secret.

Updated . 5 min read

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:

VariableDefaultMeaning
HOSTdbPostgreSQL host (the service name in Compose)
PORT5432PostgreSQL port
USERodooPostgreSQL role Odoo connects as
PASSWORDodooPassword 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:

docker-compose.yml (excerpt)
services:
  web:
    image: odoo:19.0
    environment:
      HOST: db
      USER: odoo
      PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
    volumes:
      - ./config:/etc/odoo

One 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:

.env
# Odoo stack settings
ODOO_VERSION=19.0
POSTGRES_PASSWORD=replace-with-a-strong-password
ODOO_PORT=8069
docker-compose.yml (excerpt)
services:
  web:
    image: odoo:${ODOO_VERSION:-19.0}
    ports:
      - "127.0.0.1:${ODOO_PORT:-8069}:8069"

Important points:

  • .env values are for substitution in the compose file. They do not reach the container unless you reference them under environment: or load the file with env_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 .env out of Git and commit a .env.example with placeholder values instead.

Substitution syntax

SyntaxResult
${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:

  1. docker compose run -e VAR=value on the command line.
  2. environment: or env_file: entries whose value is interpolated from your shell or an env file, such as PASSWORD: ${POSTGRES_PASSWORD}.
  3. Literal values under environment:.
  4. Values from an env_file: attribute.
  5. ENV lines 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>:

docker-compose.yml
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_pass

This 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_PASSWORD under 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: true secrets created with docker secret create belong 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 (or PASSWORD_FILE), never in odoo.conf.
  • Server settings in odoo.conf; see Odoo server configuration for workers and limits.
  • ${VAR:?} on every required value; .env and secrets/ 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.

Rather not run the server yourself?

Get managed Odoo hosting with backups, SSL, monitoring and updates handled for you. Free for 10 days.