Dockerizing Odoo ERP: Production-Ready Deployment with PostgreSQL and Nginx

Docker tutorial - IT technology blog
Docker tutorial - IT technology blog

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 exec to 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.

Share: