Skip to main content
Technical Guide

Odoo Docker Deployment: Production Guide

Deploy Odoo with Docker Compose for production environments. Complete setup with PostgreSQL, Nginx reverse proxy, SSL certificates, automated backups, and security best practices.

12-15 min read
Updated December 2024
Odoo 14-18 Compatible

Why Docker for Odoo?

Docker has become the standard for deploying Odoo in production environments. The official Odoo Docker images are well-maintained and used by organizations of all sizes worldwide. Whether you are deploying Odoo from scratch or containerizing an existing installation, Docker makes sense for Odoo deployments:

Isolation and Consistency

Docker containers ensure your Odoo installation runs identically across development, staging, and production environments. No more 'works on my machine' issues.

Easy Version Management

Switch between Odoo versions effortlessly by changing a single line in your docker-compose file. Run multiple versions side-by-side for testing upgrades.

Simplified Deployment

Deploy your entire Odoo stack with a single command. Docker Compose orchestrates Odoo, PostgreSQL, and Nginx together seamlessly.

Horizontal Scaling

When demand increases, Docker makes it straightforward to scale Odoo workers across multiple containers or servers using orchestration tools.

Prerequisites

Before starting your Odoo Docker deployment, ensure you have the following in place:

Docker Engine

Docker 20.10+ installed and running

Installation guide

Docker Compose

Docker Compose v2.0+ (included with Docker Desktop)

Installation guide

Domain Name

A registered domain pointing to your server IP

VPS or Server

Minimum 4GB RAM, 2 vCPUs, 40GB storage

Basic Linux Knowledge

Familiarity with command line and SSH

Project Structure

Create a well-organized directory structure for your Odoo Docker deployment. This structure separates configuration, custom modules, and data for maintainability:

Directory Structuretext
odoo-docker/
├── docker-compose.yml          # Main compose file
├── docker-compose.override.yml # Development overrides
├── .env                        # Environment variables
├── nginx/
│   └── odoo.conf              # Nginx configuration
├── addons/
│   └── custom_modules/        # Your custom Odoo modules
├── config/
│   └── odoo.conf              # Odoo configuration file
└── backups/
    └── .gitkeep               # Backup storage directory

Create this structure with the following commands:

mkdir -p odoo-docker/{nginx,addons/custom_modules,config,backups}
cd odoo-docker
touch docker-compose.yml .env
touch nginx/odoo.conf config/odoo.conf

Docker Compose Configuration

The docker-compose.yml file defines your entire Odoo stack. This production-ready configuration includes Odoo, PostgreSQL, and proper networking:

docker-compose.ymlyaml
version: '3.8'

services:
  odoo:
    image: odoo:17.0
    container_name: odoo
    depends_on:
      - db
    ports:
      - "8069:8069"
      - "8072:8072"  # Longpolling port
    volumes:
      - odoo-data:/var/lib/odoo
      - ./addons:/mnt/extra-addons
      - ./config:/etc/odoo
    environment:
      - HOST=db
      - USER=odoo
      - PASSWORD=${POSTGRES_PASSWORD}
    command: ["--proxy-mode"]
    restart: unless-stopped
    networks:
      - odoo-network

  db:
    image: postgres:15
    container_name: odoo-db
    environment:
      - POSTGRES_USER=odoo
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=postgres
      - PGDATA=/var/lib/postgresql/data/pgdata
    volumes:
      - postgres-data:/var/lib/postgresql/data/pgdata
    restart: unless-stopped
    networks:
      - odoo-network
    # Uncomment for external access (not recommended for production)
    # ports:
    #   - "5432:5432"

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

volumes:
  odoo-data:
  postgres-data:

networks:
  odoo-network:
    driver: bridge

Key Configuration Notes

  • - The --proxy-mode flag enables Odoo to work correctly behind a reverse proxy
  • - Port 8072 is used for longpolling (real-time features like chat)
  • - Named volumes ensure data persists across container restarts

Environment Variables

Store sensitive configuration in a .env file. Never commit this file to version control.

.envbash
# Database Configuration
POSTGRES_PASSWORD=your_secure_password_here

# Odoo Configuration (optional overrides)
ODOO_DB_HOST=db
ODOO_DB_PORT=5432
ODOO_DB_USER=odoo

# Domain Configuration
DOMAIN=erp.yourcompany.com

# Email Configuration (optional)
SMTP_HOST=smtp.yourprovider.com
SMTP_PORT=587
[email protected]
SMTP_PASSWORD=your_smtp_password

Security Warning

Use a strong, unique password for POSTGRES_PASSWORD. Add .env to your .gitignore file. In production, consider using Docker secrets or a secrets manager.

You can also create an Odoo configuration file for additional settings:

config/odoo.confini
[options]
addons_path = /mnt/extra-addons
data_dir = /var/lib/odoo

# Database settings
db_host = db
db_port = 5432
db_user = odoo
db_password = False  # Set via environment variable

# Performance tuning
workers = 4
max_cron_threads = 2
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648
limit_time_cpu = 600
limit_time_real = 1200

# Proxy settings
proxy_mode = True

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

Nginx Reverse Proxy

Nginx serves as a reverse proxy handling SSL termination, static file caching, and WebSocket connections for Odoo real-time features:

nginx/odoo.confnginx
upstream odoo {
    server odoo:8069;
}

upstream odoo-longpolling {
    server odoo:8072;
}

# HTTP - redirect all requests to HTTPS
server {
    listen 80;
    server_name erp.yourcompany.com;

    # Let's Encrypt challenge
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPS
server {
    listen 443 ssl http2;
    server_name erp.yourcompany.com;

    # SSL certificates
    ssl_certificate /etc/letsencrypt/live/erp.yourcompany.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/erp.yourcompany.com/privkey.pem;

    # SSL settings
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Proxy settings
    proxy_read_timeout 720s;
    proxy_connect_timeout 720s;
    proxy_send_timeout 720s;
    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;

    # Gzip compression
    gzip on;
    gzip_types text/css text/plain text/xml application/xml application/javascript application/json;
    gzip_min_length 1000;

    # Longpolling (real-time features)
    location /longpolling {
        proxy_pass http://odoo-longpolling;
    }

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

    # Main Odoo application
    location / {
        proxy_pass http://odoo;
        proxy_redirect off;
    }

    # Static files caching
    location ~* /web/static/ {
        proxy_pass http://odoo;
        proxy_cache_valid 200 60m;
        expires 24h;
        add_header Cache-Control "public, immutable";
    }

    # File upload size limit
    client_max_body_size 100M;
}

SSL with Lets Encrypt

Set up free SSL certificates using Certbot. First, create a temporary Nginx config for the initial certificate request, then switch to the full configuration:

Step 1: Initial Setup

# Create directories for Certbot
mkdir -p certbot/conf certbot/www

# Start Nginx with a basic config first (HTTP only)
# Create a temporary nginx config that serves HTTP
cat > nginx/odoo.conf << 'EOF'
server {
    listen 80;
    server_name erp.yourcompany.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 200 'OK';
        add_header Content-Type text/plain;
    }
}
EOF

# Start Nginx
docker compose up -d nginx

Step 2: Obtain Certificate

# Request SSL certificate
docker run -it --rm \
  -v ./certbot/conf:/etc/letsencrypt \
  -v ./certbot/www:/var/www/certbot \
  certbot/certbot certonly \
  --webroot \
  --webroot-path=/var/www/certbot \
  --email [email protected] \
  --agree-tos \
  --no-eff-email \
  -d erp.yourcompany.com

Step 3: Enable Full Configuration

# Replace nginx config with the full SSL version (shown above)
# Then restart Nginx
docker compose restart nginx

# Set up auto-renewal (add to crontab)
0 0 1 * * docker run --rm -v ./certbot/conf:/etc/letsencrypt -v ./certbot/www:/var/www/certbot certbot/certbot renew --quiet && docker compose restart nginx

Data Persistence

Docker volumes ensure your data persists across container restarts and updates. Understanding volume management is critical for production deployments:

odoo-data Volume

Contains the Odoo filestore including attachments, session files, and uploaded documents.

/var/lib/odoo

postgres-data Volume

Contains the PostgreSQL database files with all your business data.

/var/lib/postgresql/data
# List Docker volumes
docker volume ls

# Inspect a volume
docker volume inspect odoo-docker_odoo-data

# Find volume location on host
docker volume inspect odoo-docker_postgres-data --format '{{ .Mountpoint }}'

Backup Strategy

A robust backup strategy is essential for production Odoo deployments. Back up both the database and filestore regularly. For comprehensive backup strategies including automated off-site storage, see our complete Odoo backup and recovery guide:

Database Backup Script

backup.shbash
#!/bin/bash

# Configuration
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)
DB_CONTAINER="odoo-db"
DB_USER="odoo"
RETENTION_DAYS=30

# Create backup directory
mkdir -p $BACKUP_DIR

# Backup database
echo "Backing up database..."
docker exec $DB_CONTAINER pg_dumpall -U $DB_USER | gzip > "$BACKUP_DIR/db_backup_$DATE.sql.gz"

# Backup Odoo filestore
echo "Backing up filestore..."
docker run --rm \
  -v odoo-docker_odoo-data:/data:ro \
  -v $(pwd)/$BACKUP_DIR:/backup \
  alpine tar czf /backup/filestore_backup_$DATE.tar.gz -C /data .

# Remove old backups
echo "Removing backups older than $RETENTION_DAYS days..."
find $BACKUP_DIR -name "*.gz" -mtime +$RETENTION_DAYS -delete

echo "Backup completed: $DATE"
echo "Files:"
ls -lh $BACKUP_DIR/*_$DATE.*

Restore from Backup

# Restore database
gunzip -c backups/db_backup_20241220_030000.sql.gz | docker exec -i odoo-db psql -U odoo

# Restore filestore
docker run --rm \
  -v odoo-docker_odoo-data:/data \
  -v $(pwd)/backups:/backup:ro \
  alpine tar xzf /backup/filestore_backup_20241220_030000.tar.gz -C /data

Off-site Backup with S3

# Install AWS CLI and configure credentials
# Then add to your backup script:

# Upload to S3
aws s3 cp $BACKUP_DIR/db_backup_$DATE.sql.gz s3://your-bucket/odoo-backups/
aws s3 cp $BACKUP_DIR/filestore_backup_$DATE.tar.gz s3://your-bucket/odoo-backups/

# Or use rclone for other providers (Backblaze B2, Cloudflare R2, etc.)
rclone copy $BACKUP_DIR remote:odoo-backups/

Backup Best Practices

  • - Automate backups using cron (daily minimum)
  • - Store backups off-site (different location/provider)
  • - Test restore procedures regularly
  • - Encrypt sensitive backups before upload

Scaling Considerations

As your Odoo deployment grows, you will need to scale resources. For detailed performance optimization techniques, here are key considerations for production scaling:

Horizontal Scaling with Workers

Odoo supports multiple worker processes to handle concurrent requests. Configure workers based on CPU cores:

# In odoo.conf - Rule of thumb: (CPU cores * 2) + 1
workers = 4
max_cron_threads = 2
limit_memory_hard = 2684354560
limit_memory_soft = 2147483648

Load Balancing

For high-availability deployments, use multiple Odoo containers behind a load balancer. Enable sticky sessions for session consistency:

upstream odoo_cluster {
    ip_hash;  # Sticky sessions
    server odoo1:8069;
    server odoo2:8069;
    server odoo3:8069;
}

Redis for Session Storage

When scaling horizontally, use Redis for shared session storage across Odoo instances:

services:
  redis:
    image: redis:7-alpine
    restart: unless-stopped
    volumes:
      - redis-data:/data
    networks:
      - odoo-network

When to Use Managed Hosting

Self-managing Docker deployments provides maximum control but comes with significant operational overhead. Consider managed hosting when:

Self-Managed Docker

  • Full control over infrastructure
  • Potentially lower hosting costs
  • Requires DevOps expertise
  • Manual backup management
  • Security updates on you

Managed Hosting (OEC.sh)

  • Automated backups and SSL
  • Choose from 14+ cloud providers
  • Git integration for deployments
  • Monitoring and alerting built-in
  • Focus on your business, not servers

Managing Docker infrastructure is not your core business?

OEC.sh handles all the complexity of Odoo Docker deployments while giving you full control. Deploy to your preferred cloud provider in minutes, not hours.

  • Free tier available
  • No credit card required
  • 5-minute setup
Try OEC.sh Free

Frequently Asked Questions

Is Docker good for Odoo production?

Yes, Docker is excellent for production Odoo deployments when configured properly. Official Odoo Docker images are maintained and used by thousands of organizations worldwide. The key is proper configuration of persistent volumes, backups, security settings, and resource limits. Docker provides consistency across environments, easy version management, and simplified deployment. For mission-critical deployments, consider managed solutions like OEC.sh that handle the operational complexity while giving you the benefits of Docker.

What's the best Docker image for Odoo?

The official Odoo Docker images from Docker Hub (odoo:17.0, odoo:16.0, etc.) are the best choice for most deployments. These images are maintained by Odoo S.A., regularly updated, and optimized for production use. They include all necessary dependencies and follow Docker best practices. For Enterprise, you'll need to build a custom image extending the official Community image. Avoid unofficial third-party images as they may have security vulnerabilities or lack proper maintenance.

How do I backup Odoo in Docker?

Backup both the PostgreSQL database and Odoo filestore. For the database, use 'docker exec db pg_dump -U odoo postgres > backup.sql'. For filestore, backup the mounted odoo-data volume using 'docker run --rm -v odoo-data:/data -v ./backups:/backup alpine tar czf /backup/filestore.tar.gz /data'. Automate with cron and store backups off-site using S3, Backblaze B2, or Cloudflare R2. OEC.sh provides automated daily backups to 7+ storage providers with one-click restore. For a complete guide, see our Odoo backup and recovery documentation.

Should I use Docker Compose for Odoo?

Yes, Docker Compose is the recommended way to run Odoo in Docker for most deployments. It simplifies orchestrating multiple containers (Odoo, PostgreSQL, Nginx) and manages networking, volumes, and environment variables in a single file. Docker Compose makes it easy to version control your infrastructure configuration. For production at scale, consider Kubernetes or managed platforms like OEC.sh, but Docker Compose is perfect for small to medium deployments and development environments.

How many workers should I configure in Docker?

For production Odoo in Docker, use the formula: workers = (CPU cores * 2) + 1. For example, 4 CPU cores = 9 workers. Each worker consumes 150-300MB RAM, so ensure your container has sufficient memory limits. Set this in your odoo.conf file mounted into the container. Also configure max_cron_threads (typically 2), limit_memory_hard (2.5GB per worker), and limit_memory_soft (2GB per worker). Monitor resource usage and adjust based on actual workload. For detailed performance tuning, see our Odoo performance optimization guide.

How to scale Odoo with Docker?

Scale Odoo horizontally by running multiple Odoo containers behind a load balancer (Nginx or HAProxy) with sticky sessions enabled. Share the PostgreSQL database and filestore volume across instances, or use Redis for session storage. Configure your docker-compose.yml to run multiple replicas: 'docker compose up --scale odoo=3'. For high availability, use Docker Swarm or Kubernetes. However, scaling Docker manually requires expertise - managed platforms like OEC.sh handle this automatically with one-click horizontal scaling across multiple providers.

How do I update Odoo in Docker?

To update Odoo in Docker: (1) Backup your database and filestore first, (2) Pull the new image with 'docker pull odoo:17.0', (3) Stop the current container with 'docker compose down', (4) Update the image tag in docker-compose.yml, (5) Start with 'docker compose up -d'. For major version upgrades (e.g., 16.0 to 17.0), use Odoo's database migration tools or OpenUpgrade first. Always test upgrades in a staging environment before applying to production.

Can I run multiple Odoo instances in Docker?

Yes, run multiple Odoo instances by creating separate docker-compose files in different directories, or use different service names and ports. Each instance needs its own database (can share the PostgreSQL server). Use Nginx virtual hosts to route traffic based on domain names. This is useful for running multiple companies, staging environments, or different Odoo versions side-by-side. For complex multi-tenant setups, consider OEC.sh which manages multiple instances with isolated environments.

Need Help with Your Odoo Docker Deployment?

Whether you choose self-managed Docker or our managed platform, we are here to help you succeed with Odoo.