Skip to main content

Docker Odoo Deployment: Production-Ready Container Setup

Deploy Odoo in Docker containers for production. Complete guide covering Docker Compose configuration, PostgreSQL setup, Nginx reverse proxy, SSL, backups, and scaling.

E
ECOSIRE Research and Development Team
|March 5, 20265 min read852 Words|

Docker has become the standard deployment method for Odoo in production environments, with over 10 million Odoo Docker image pulls on Docker Hub. Containerized deployment provides reproducible environments, easy scaling, simplified backups, and clean separation between Odoo instances.

Docker has become the standard deployment method for Odoo in production environments, with over 10 million Odoo Docker image pulls on Docker Hub. Containerized deployment provides reproducible environments, easy scaling, simplified backups, and clean separation between Odoo instances. This guide covers a complete production-ready Docker Odoo setup.

Key Takeaways

  • Docker Compose orchestrates Odoo, PostgreSQL, and Nginx as connected services
  • Persistent volumes ensure data survives container restarts and updates
  • Nginx reverse proxy handles SSL termination, static file caching, and load balancing
  • Automated backups protect against data loss with minimal manual intervention

Architecture Overview

A production Odoo Docker deployment consists of three core services:

  1. Odoo application server: The Odoo container running the web server and application logic
  2. PostgreSQL database: A dedicated database container storing all Odoo data
  3. Nginx reverse proxy: Handles SSL, serves static files, and proxies requests to Odoo

Optional services include Redis (for caching and session storage), a backup container (automated database dumps), and monitoring (Prometheus + Grafana).


Docker Compose Configuration

The foundation of the deployment is a docker-compose.yml file defining all services:

version: "3.8"

services:
  odoo:
    image: odoo:17.0
    depends_on:
      - db
    ports:
      - "8069:8069"
      - "8072:8072"
    volumes:
      - odoo-data:/var/lib/odoo
      - ./config:/etc/odoo
      - ./addons:/mnt/extra-addons
    environment:
      - HOST=db
      - PORT=5432
      - USER=odoo
      - PASSWORD=odoo_db_password
    restart: unless-stopped

  db:
    image: postgres:16
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=odoo
      - POSTGRES_PASSWORD=odoo_db_password
      - PGDATA=/var/lib/postgresql/data/pgdata
    volumes:
      - postgres-data:/var/lib/postgresql/data/pgdata
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./certbot/conf:/etc/letsencrypt
      - ./certbot/www:/var/www/certbot
    depends_on:
      - odoo
    restart: unless-stopped

volumes:
  odoo-data:
  postgres-data:

Odoo Configuration File

Create config/odoo.conf with production-optimized settings:

[options]
addons_path = /mnt/extra-addons,/usr/lib/python3/dist-packages/odoo/addons
admin_passwd = your_strong_admin_password
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo_db_password
db_name = production_db
dbfilter = ^production_db$
list_db = False
proxy_mode = True
workers = 4
max_cron_threads = 2
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_time_cpu = 600
limit_time_real = 1200
log_level = warn

Key settings explained:

  • proxy_mode = True: Required when running behind Nginx to correctly handle forwarded headers
  • workers = 4: Number of worker processes (rule of thumb: 2 workers per CPU core)
  • max_cron_threads = 2: Dedicated threads for scheduled actions
  • list_db = False: Prevents database listing on the login page (security)
  • dbfilter: Restricts access to a single database

Nginx Reverse Proxy Configuration

upstream odoo {
    server odoo:8069;
}
upstream odoochat {
    server odoo:8072;
}

server {
    listen 80;
    server_name your-domain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;

    client_max_body_size 200m;
    proxy_read_timeout 720s;
    proxy_connect_timeout 720s;
    proxy_send_timeout 720s;

    location /websocket {
        proxy_pass http://odoochat;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location / {
        proxy_pass http://odoo;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location ~* /web/static/ {
        proxy_cache_valid 200 90m;
        proxy_buffering on;
        expires 864000;
        proxy_pass http://odoo;
    }
}

Backup Strategy

Automated Database Backups

Create a backup script that runs daily via cron:

#!/bin/bash
BACKUP_DIR=/backups/odoo
DATE=$(date +%Y%m%d_%H%M%S)
docker exec odoo-db pg_dump -U odoo production_db | gzip > $BACKUP_DIR/odoo_$DATE.sql.gz
find $BACKUP_DIR -name "*.sql.gz" -mtime +30 -delete

Filestore Backup

The Odoo filestore (uploaded documents, attachments) lives in the odoo-data volume. Back it up alongside the database:

docker run --rm -v odoo-data:/data -v /backups:/backup alpine \
  tar czf /backup/odoo-filestore_$DATE.tar.gz /data

Security Hardening

  • Change default admin_passwd in odoo.conf to a strong random password
  • Set list_db = False to prevent database enumeration
  • Use dbfilter to restrict database access to a single database
  • Run containers as non-root users where possible
  • Keep Docker images updated with security patches
  • Use Docker secrets or environment variable files (not inline passwords)
  • Restrict container network access using Docker networks

Frequently Asked Questions

Q: How many concurrent users can a single Odoo container handle?

With 4 workers, an Odoo container handles approximately 20-30 concurrent users comfortably. For higher concurrency, increase workers (limited by CPU/RAM) or deploy multiple Odoo containers behind a load balancer.

Q: How do I update Odoo in Docker?

Pull the new image, stop the current container, run the new image with the existing volumes. The persistent volumes retain your data and filestore across image updates.

Q: Should I use the official Odoo Docker image or build my own?

The official image works for standard deployments. Build a custom image when you need specific Python packages, system dependencies for custom modules, or pre-installed community modules.

Q: How do I handle Odoo Enterprise in Docker?

Odoo Enterprise is not available as a public Docker image. Mount the enterprise addons directory as a volume and reference it in the addons_path configuration. You need a valid Odoo Enterprise subscription.


What Is Next

Docker deployment gives you a reproducible, scalable, and maintainable Odoo production environment. Start with this configuration and adjust worker counts, memory limits, and caching based on your usage patterns.

Contact ECOSIRE for Odoo deployment assistance, or explore our Odoo support services for managed hosting.


Published by ECOSIRE -- helping businesses scale with enterprise software solutions.

E

Written by

ECOSIRE Team

Technical Writing

The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.

ECOSIRE

Transform Your Business with Odoo ERP

Expert Odoo implementation, customization, and support to streamline your operations.