An Odoo 20 Community image for hosted platforms such as Railway, Render, or Kubernetes, and for local Docker Compose. This fork of iterativo-git/dockerdoo configures Odoo through the environment variables Odoo 20 reads natively and adds only what platforms need before Odoo starts.
ghcr.io/indexa-labs/dockerdoo:20.0 is the newest tested build, and 20.0.YYYYMMDD names one build. The dated tag is valid semver, so platforms that auto-update by semver follow dated builds. Pin a dated tag or a digest in production, and verify it with:
gh attestation verify oci://ghcr.io/indexa-labs/dockerdoo:20.0 --owner indexa-labsGitHub Actions builds the current Odoo 20.0 branch weekly and on every change, for linux/amd64 and linux/arm64. It publishes only after tests/test_hosted.py and Odoo's own base tests pass on both. The image installs Odoo's pinned Python requirements and has no sudo or developer tools.
Odoo 20 reads every server option from ODOO_<OPTION> (for example ODOO_WORKERS, ODOO_PROXY_MODE, ODOO_SMTP_SERVER) and the database connection from PGHOST, PGPORT, PGUSER, PGPASSWORD, and PGDATABASE. The image sets only these defaults:
| Variable | Default |
|---|---|
ODOO_HTTP_PORT |
The platform's PORT, else 8069 |
ODOO_DBFILTER |
^$PGDATABASE$ when PGDATABASE is set |
ODOO_ADMIN_PASSWD |
A random master password per container |
ODOO_LIST_DB |
False |
ODOO_HTTP_INTERFACE |
0.0.0.0 (Odoo's own default is 127.0.0.1) |
ODOO_ADDONS_PATH |
Add-on folders found under /mnt/extra-addons, then Odoo's add-ons |
ODOO_DATA_DIR |
/var/lib/odoo/data; mount the volume here |
Add-on options that Odoo has no variable for go in ODOO_EXTRA_OPTIONS, one key = value per line; the image writes them to the configuration file.
Before the server starts, the image does what hosted platforms need and Odoo does not:
Database creation and upgrades run under a PostgreSQL advisory lock, so containers started together change a database once. The container starts as root, takes ownership of ODOO_DATA_DIR if needed, and drops to the odoo user; set ODOO_RUN_AS_ROOT=1 to keep root, or start it as uid 1000 on a volume it already owns.
Odoo 20 serves websockets on a separate port when ODOO_WORKERS is above zero. A platform that routes one port per service needs ODOO_WORKERS=0 or a reverse proxy in front of both ports.
Add-ons in ./custom are copied into the image at build time, and folders mounted under /mnt/extra-addons are found at start. To ship your own add-ons and Python dependencies, build on a pinned digest:
FROM ghcr.io/indexa-labs/dockerdoo:20.0.YYYYMMDD@sha256:...
COPY requirements.txt /tmp/requirements.txt
RUN pip install --no-cache-dir --require-hashes -r /tmp/requirements.txt
COPY --chown=odoo:odoo my_addons/ /mnt/extra-addons/my_addons/
ENV AUTO_INIT=1 INIT_MODULES=my_moduleCopy .env.example to .env, set the passwords, and run docker compose up. Odoo answers on http://127.0.0.1:8069.
To test a local build the way CI does:
docker build --target production --build-arg ODOO_VERSION=20.0 -t dockerdoo-local:20.0 .
python3 tests/test_hosted.py --image dockerdoo-local:20.0 --native-testsBased on iterativo-git/dockerdoo, itself mainly based on dockery-odoo work by David Arnold (XOE Solutions), with ideas from Odoo (docker), OCA (maintainer-quality-tools), and Ingeniería ADHOC (docker-odoo-adhoc).
