Traditional Odoo Installation: The Nightmare Called “Dependency Hell”
If you’ve ever installed Odoo directly on Ubuntu, you’ve likely experienced the frustration of staying up all night due to library conflicts. Odoo requires dozens of system packages like libxml2 or libxslt. Just a slight mismatch in the Python version can bring your entire ERP system to a standstill.
I once spent nearly 8 hours troubleshooting wkhtmltopdf errors while upgrading a server. With Docker, everything changes. You only need a single configuration file to deploy the system to any server in under 5 minutes.
Why One Container Isn’t Enough
Many beginners often cram Odoo and PostgreSQL into a single image (All-in-one). This is a critical mistake in a production environment. As data grows to dozens of GBs, backing up and scaling becomes a nightmare.
A multi-container model separates components, making the system much more flexible. Here is a practical comparison:
| Criteria | All-in-one (Testing) | Multi-container (Production) |
|---|---|---|
| Scalability | Practically zero | Easily upgrade DB RAM or add app replicas |
| Data Safety | High risk | Database resides in an internal network, completely isolated |
| Deployment Speed | Fast but hard to modify | Standardized, easy CI/CD integration |
Setting Up a Logical Directory Structure
Before running any commands, prepare a clear directory structure. This helps you manage custom modules and data without confusion as the system grows.
mkdir odoo-deployment && cd odoo-deployment
mkdir -p ./config ./addons ./data/postgres ./data/odoo
touch docker-compose.yml ./config/odoo.conf ./config/nginx.conf
addons/: Where custom or store-bought modules are stored.data/: Retains all invoices and documents even if you delete the container.config/: The heart that controls all operational parameters.
Building a Proper Docker Compose File
Below is the configuration for three services: db (PostgreSQL 15), odoo (v17), and webserver (Nginx). Note how we connect them through a dedicated network.
version: '3.8'
services:
db:
image: postgres:15
environment:
- POSTGRES_DB=postgres
- POSTGRES_PASSWORD=odoo_password_secret
- POSTGRES_USER=odoo
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- odoo-nw
odoo:
image: odoo:17.0
depends_on:
- db
ports:
- "8069:8069"
volumes:
- ./data/odoo:/var/lib/odoo
- ./config:/etc/odoo
- ./addons:/mnt/extra-addons
environment:
- HOST=db
- USER=odoo
- PASSWORD=odoo_password_secret
networks:
- odoo-nw
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./config/nginx.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- odoo
networks:
- odoo-nw
networks:
odoo-nw:
driver: bridge
Pro tip: The depends_on parameter is crucial. It ensures the database is fully started before Odoo begins running, preventing connection errors immediately upon startup.
Nginx Configuration: The Key to Smooth HTTPS
To ensure Odoo correctly identifies the user’s IP address, you must enable proxy_mode = True in the ./config/odoo.conf file. If you forget this line, you will encounter constant session errors when using HTTPS.
[options]
admin_passwd = my_admin_password
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo_password_secret
proxy_mode = True
Regarding Nginx, which functions as a reverse proxy, pay attention to the proxy_read_timeout configuration. Heavy Odoo reports often take more than 60 seconds to process. If left at the default, Nginx will disconnect and return a 504 error.
upstream odoo {
server odoo:8069;
}
server {
listen 80;
server_name your-domain.com;
proxy_read_timeout 720s;
proxy_connect_timeout 720s;
location / {
proxy_pass http://odoo;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Operation and Testing
Activate the entire system with a single command:
docker-compose up -d
Use docker-compose ps to check the status, or simplify management with LazyDocker. If everything shows Up, you’ve succeeded. When debugging long JSON configuration files, I often use JSON Formatter to quickly check for syntax errors in the browser.
Real-world Experience to Avoid Sleepless Nights
After many client deployments, I’ve distilled three golden rules to keep the system running stably 24/7, such as integrating Docker containers with systemd and configuring Docker log rotation:
- Worker Calculation Formula: Don’t stick with the default. Use the formula
(number of CPU cores * 2) + 1. A 4-core server should have 9 workers to optimize performance. - Backup Strategy: Never rely solely on Docker Volumes. Use
docker execto dump the SQL file every night and push it to S3 or Google Drive. - Real-time Processing: When running multi-worker, you need to open an additional port 8072 (Longpolling). Without it, message notifications in Odoo won’t appear instantly.
Dockerizing Odoo isn’t difficult; the challenge lies in configuring it correctly for production. Hopefully, this guide helps you build a robust and easy-to-manage ERP system.

