Skip to content

Deploy Odoo with Docker: Complete Production Guide

DeployMonkey Team · March 22, 2026 16 min read

Why Docker for Odoo?

Docker provides consistent, reproducible Odoo deployments. The same configuration runs identically on development, staging, and production. Updates are clean: pull the new image, restart, done. Rollbacks are instant: revert to the previous image. Multiple Odoo instances (different versions, different databases) coexist cleanly on the same server.

Docker Compose Setup

# docker-compose.yml
version: '3.8'

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

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

volumes:
  odoo-data:
  postgres-data:

Production odoo.conf

[options]
addons_path = /mnt/extra-addons,/usr/lib/python3/dist-packages/odoo/addons
db_host = db
db_port = 5432
db_user = odoo
db_password = odoo_password
db_name = production

# Workers
workers = 5
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_time_real = 120
limit_time_cpu = 60

# Security
list_db = False
admin_passwd = change_this_to_a_strong_password
dbfilter = ^production$

# Proxy
proxy_mode = True

# Longpolling
gevent_port = 8072

# Logging
log_level = info
logfile = /var/log/odoo/odoo.log

Nginx Reverse Proxy

# /etc/nginx/sites-available/odoo
upstream odoo {
    server 127.0.0.1:8069;
}

upstream odoo-longpolling {
    server 127.0.0.1:8072;
}

server {
    listen 80;
    server_name your-domain.com;
    return 301 https://$server_name$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;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";
    add_header Strict-Transport-Security "max-age=31536000";

    # Proxy settings
    proxy_set_header 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;

    # Longpolling
    location /longpolling {
        proxy_pass http://odoo-longpolling;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # Block database manager
    location /web/database {
        deny all;
        return 404;
    }

    # Main application
    location / {
        proxy_pass http://odoo;
        proxy_read_timeout 720s;
        proxy_connect_timeout 720s;
        proxy_send_timeout 720s;
        client_max_body_size 200m;
    }

    # Static files caching
    location ~* /web/static/ {
        proxy_pass http://odoo;
        expires 7d;
        add_header Cache-Control "public, immutable";
    }
}

Custom Module Deployment

# Directory structure:
./custom-addons/
├── my_module/
│   ├── __manifest__.py
│   ├── __init__.py
│   ├── models/
│   ├── views/
│   └── security/
└── another_module/

# Deploy custom modules:
# 1. Copy modules to custom-addons/
# 2. Restart Odoo
docker compose restart odoo

# 3. Update modules
docker compose exec odoo odoo -d production -u my_module --stop-after-init

Backup Automation

#!/bin/bash
# backup.sh — run via cron daily
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=/opt/backups

# Database backup
docker compose exec -T db pg_dump -U odoo production | gzip > $BACKUP_DIR/db_$DATE.sql.gz

# Filestore backup
docker compose exec -T odoo tar czf - /var/lib/odoo/filestore/production > $BACKUP_DIR/filestore_$DATE.tar.gz

# Upload to S3 (optional)
aws s3 cp $BACKUP_DIR/db_$DATE.sql.gz s3://your-bucket/backups/
aws s3 cp $BACKUP_DIR/filestore_$DATE.tar.gz s3://your-bucket/backups/

# Retention: keep 30 days
find $BACKUP_DIR -mtime +30 -delete

Updates

# Update Odoo to latest patch
docker compose pull odoo
docker compose up -d odoo

# Update to new version (e.g., 18 → 19)
# 1. Backup
# 2. Change image tag in docker-compose.yml
# 3. docker compose pull
# 4. docker compose up -d
# 5. Run module updates

Multi-Instance Setup

# Run multiple Odoo instances on one server:
# Instance 1: production (port 8069)
# Instance 2: staging (port 8169)
# Use separate docker-compose files or multiple services

Or Just Use DeployMonkey

All of the above — Docker setup, nginx, SSL, backups, updates, monitoring — is handled automatically by DeployMonkey. Deploy Odoo in 5 minutes without configuring any of this yourself. Free plan available.