The short answer
Odoo runs well in Docker in production. But a compose file that works on a laptop isn’t ready yet. You need a few more things. Pin the image. Fit the workers to the container’s memory. Put the filestore on a volume. Give Postgres room. And build staging from the same image.
Intro
Docker is a good way to run Odoo. Each Odoo gets its own space, you can rebuild it the same way each time, and staging can match production. That’s why many teams choose it.
The official Odoo image gets you started in minutes. This guide is about what comes next: the six things that change when real people use it all day.
1. Isolation: one Odoo per container
Run each Odoo in its own container, with its own config file and its own database user. Two clients, or production and staging, should never share a container.
Inside each one, close the doors you don’t need:
[options]
admin_passwd = A_LONG_RANDOM_PASSWORD
list_db = False
dbfilter = ^yourdb$
proxy_mode = True
list_db = False hides the database manager. dbfilter makes sure this container only ever opens its own database.
Only your proxy should reach ports 8069 and 8072. Don’t publish them to the internet.
2. Reproducible builds
“It worked yesterday” is often a moving image tag. odoo:18.0 today is not the same image as odoo:18.0 next month, because Odoo ships fixes into it.
Build your own image on top of the official one, pin it, and add your Python packages there:
FROM odoo:18.0@sha256:<digest-you-tested>
USER root
COPY requirements.txt /tmp/requirements.txt
RUN pip3 install --no-cache-dir --break-system-packages -r /tmp/requirements.txt
USER odoo
Pin the versions in requirements.txt too. Then the image you tested is the image that runs. When you want Odoo’s newest fixes, change the digest on purpose, test it on staging, and roll it out.
Keep your modules out of the image’s own code. Mount them at /mnt/extra-addons, or copy them in at build time. Never edit files inside a running container. The next restart throws those changes away.
3. Staging that matches production
With Docker, staging can be the same as production. Same image, same config, same modules. Only the data and the domain change.
For the data, copy the database and the files from production. Then clean the copy before Odoo starts. A test Odoo with real customer emails and a live mail server can email your real customers. Our guide on copying production Odoo to staging without emailing your customers shows what to switch off.
4. Workers and container memory
With workers = 0, Odoo runs as one process. Fine for a laptop, too slow for a team. In production, set workers above 0.
workers = 4
max_cron_threads = 1
gevent_port = 8072
limit_memory_soft = 2147483648
limit_memory_hard = 2684354560
Here is the Docker trap. Odoo’s memory limits are per worker. Docker’s memory limit is for the whole container. If all workers together can use more than the container’s limit, the kernel kills the container. Odoo doesn’t get to restart one worker. Everything goes down at once, often at the busiest time of day.
So size it from the top. Take the container’s memory, keep some for the main process and cron, and split the rest between workers. Our guide on how many Odoo workers you need walks through the numbers.
Also, live chat and live updates use a websocket on port 8072. Your proxy has to send /websocket there. See the proxy mode guide for the full config.
5. The filestore
Attachments, images and PDFs are not in the database. Odoo keeps them in the filestore, under /var/lib/odoo in the official image.
If that path isn’t on a named volume, your files live inside the container. Recreate the container, and they’re gone. The database still opens, so nobody notices until someone clicks on an old invoice.
volumes:
- odoo-data:/var/lib/odoo
Back it up with the database, at the same moment. A database from Monday with files from Friday doesn’t match.
6. Postgres
You can run Postgres in its own container next to Odoo, or use a managed database. Either works. A few things matter in Docker:
- Put Postgres data on a named volume, and never remove that volume by mistake with docker compose down -v.
- Give Postgres more shared memory. Docker’s default /dev/shm is 64 MB, and bigger queries can fail with a “could not resize shared memory segment” error.
- Restore databases so the Odoo user owns them. On almost any other app, a plain pg_restore is fine. With Odoo, a database owned by the wrong user disappears from Odoo’s database manager.
A compose file with all six points in it:
services:
db:
image: postgres:16
environment:
POSTGRES_USER: odoo
POSTGRES_PASSWORD: CHANGE_ME
POSTGRES_DB: postgres
shm_size: 256mb
volumes:
- db-data:/var/lib/postgresql/data
restart: unless-stopped
odoo:
image: registry.example.com/our-odoo:2026-10-01
depends_on: [db]
environment:
HOST: db
USER: odoo
PASSWORD: CHANGE_ME
volumes:
- ./config/odoo.conf:/etc/odoo/odoo.conf:ro
- ./addons:/mnt/extra-addons:ro
- odoo-data:/var/lib/odoo
ports:
- "127.0.0.1:8069:8069"
- "127.0.0.1:8072:8072"
mem_limit: 6g
restart: unless-stopped
volumes:
db-data:
odoo-data:
To update a module, stop Odoo and run the update in a one-off container:
docker compose stop odoo
docker compose run --rm odoo odoo -d yourdb -u your_module --stop-after-init
docker compose start odoo
Take a backup of the database and the filestore first. If the update fails half way, the old image won’t fit the changed database.
What is still your responsibility
Docker makes Odoo easier to rebuild. It doesn’t run Odoo for you. Backups, restore tests, updates, staging copies, log rotation, disk space and watching for problems are still jobs someone does each week. Each Odoo trap shows up one at a time, and learning them all takes months.
How Skysize runs Odoo in Docker
We run every Odoo this way, on our cloud or on your own server.
- Each Odoo runs in its own container, with its own database.
- Each build gets a fresh copy of your code. No two containers share a folder.
- Staging runs from the same setup as production, with private data scrubbed.
- Before each update, your database is copied. If Odoo doesn’t come back healthy, it goes back to how it was.
See how branches become environments. If you’d like to keep your own server, see bring your own server: you keep the server, and we run Odoo on it. Our software connects out from your server, so you don’t open any port to us.
Skysize is built by ex-Odoo engineers and is ISO 27001 certified.
Talk to an Odoo hosting expert
FAQ
Is Docker good for running Odoo in production? Yes. It gives each Odoo its own space and makes staging match production. You still need pinned images, sized workers, a filestore volume and tested backups.
Where is the filestore in the official Odoo Docker image? Under /var/lib/odoo. Put it on a named volume, or your files are lost when the container is recreated.
Why does my Odoo container restart under load? Often the workers can use more memory together than the container’s memory limit, so the kernel stops the whole container. Lower the workers or their memory limits, or give the container more memory.
Should Postgres run in Docker too? It can. Use a named volume for its data and give it more shared memory. A managed database also works.
How do I update a module in Odoo on Docker? Stop Odoo, back up the database and filestore, then run odoo -d yourdb -u your_module --stop-after-init in a one-off container.