Skip to main content

Custom Docker image

Run the Odoo deployments on your server from your own Docker image, derived from the official one. This is how you add apt packages, system libraries, fonts or compiled Python packages: containers run as an unprivileged user with pip only, so anything that needs root at build time goes into the image.

If a plain Python package is all you need, a requirements.txt in your repository is still the simplest option; it is installed on every deploy without any image work.


Build the image

Start from the official image of the Odoo version you deploy and install what you need as root, then switch back to user odoo. A typical example, adding the CUPS printing library:

FROM odoo:19.0
USER root
RUN apt-get update \
&& apt-get install -y --no-install-recommends libcups2 libcups2-dev gcc python3-dev \
&& pip3 install --break-system-packages pycups \
&& apt-get purge -y gcc python3-dev libcups2-dev && apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/*
USER odoo

Keep the image's layout unchanged: odoo on the PATH, data in /var/lib/odoo, standard addons under /usr/lib/python3/dist-packages/odoo/addons, and pip3 available. Everything else stays the platform's job: your repository's code and requirements.txt are still mounted and installed on top.

Add Odoo Enterprise to the image

To ship Odoo Enterprise inside your image, copy the Enterprise modules into the standard addons folder, /usr/lib/python3/dist-packages/odoo/addons. It is the only folder that works: the platform starts Odoo with its own addons path, so an addons_path set in the image's odoo.conf is ignored, and anything under /mnt/extra-addons is hidden by your repository's code.

Put the Enterprise source for your Odoo version next to your Dockerfile, for example with a GitHub account linked to your Odoo Enterprise subscription:

git clone --depth 1 --branch 19.0 https://github.com/odoo/enterprise.git

Then copy the module folders only. The root of the Enterprise repository contains files, including an __init__.py, that would break Odoo's addons folder and prevent Odoo from starting:

FROM odoo:19.0 AS enterprise
USER root
COPY enterprise/ /src/enterprise/
RUN rm -rf /src/enterprise/.git \
&& find /src/enterprise -mindepth 1 -maxdepth 1 ! -type d -delete

FROM odoo:19.0
COPY --from=enterprise --chown=odoo:odoo /src/enterprise/ /usr/lib/python3/dist-packages/odoo/addons/

Check the result before attaching the image; the command must list the folder:

docker run --rm --entrypoint ls mycompany/odoo:19.0 -d /usr/lib/python3/dist-packages/odoo/addons/web_enterprise

Never put your GitHub token in the Dockerfile or in the image.

Build one tag per Odoo version you deploy, always <repository>:<version>:

docker build -t mycompany/odoo:19.0 .

You can build directly on the server, or push the image to a registry the server can pull from anonymously.


Attach it to your server

  1. Open your server's page in the dashboard and find Custom Docker image.
  2. Enter the repository without a tag, for example mycompany/odoo or registry.mycompany.com/odoo.
  3. If your image contains Odoo Enterprise (added as described above), turn on The image already contains Odoo Enterprise.
  4. Save.

Nothing restarts when you save. The image is used on the next deploy, update or restart of each branch on that server, and the build log shows the image being pulled. Rebuilding under the same tag is picked up the same way.

When a pull fails, the local image of that name is used if it exists on the server; if it does not, the deployment fails with a message telling you to build or push <repository>:<version>. This requires agent version 1.4.0 or later on the server.


Odoo Enterprise

Three cases:

Your server has your GitHub tokenImage contains EnterpriseResult
yesanyYour image, with the Enterprise code downloaded with your token added as well; Enterprise modules already inside the image take precedence
noyesYour image as is; nothing is downloaded
nonoYour image is skipped for Enterprise projects, which fall back to the platform image

See Odoo Enterprise on your own server for the token.


Good to know

  • A broken image fails the post-deploy health check and the deployment is rolled back like any failed update; the reason is in the build log.
  • Only the branches on that server use the image. Managed cloud hosting always uses the platform image.
  • Rebuilding under the same tag re-points the tag; the previous image becomes unused and is cleaned up automatically after the branches have moved to the new one.
  • Private registries: the server's Docker daemon can hold its own registry login; Skysize never needs the credentials.