Odoo is an open-source business suite: CRM, sales, invoicing and accounting, inventory, e-commerce, website builder, projects, HR and dozens more apps, all sharing one database. The Community Edition is free and open source. The Enterprise Edition adds more apps and support. Self-hosting Odoo on a VPS gives you full control over data and customisation.

Sizing

Users / load VPS
A few users, light use 2 vCPU, 4 GB RAM
10–50 users 4 vCPU, 8 GB RAM
Larger, or with a website shop 8+ vCPU, 16 GB+, tuned PostgreSQL, maybe a separate DB server

Odoo is Python with PostgreSQL. Disk needs depend on attachments (the filestore).

Docker Compose

# /opt/odoo/compose.yaml
services:
  odoo:
    image: odoo:18
    restart: unless-stopped
    depends_on: [db]
    ports:
      - "127.0.0.1:8069:8069"
      - "127.0.0.1:8072:8072"     # websocket / longpolling (when workers > 0)
    volumes:
      - odoo-data:/var/lib/odoo
      - ./config:/etc/odoo
      - ./addons:/mnt/extra-addons
    environment:
      HOST: db
      USER: odoo
      PASSWORD: change-me-db
  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: odoo
      POSTGRES_PASSWORD: change-me-db
    volumes:
      - pg-data:/var/lib/postgresql/data
volumes:
  odoo-data:
  pg-data:

Use the Odoo version you need (17, 18…). Upgrades between major versions require data migration.

odoo.conf essentials

./config/odoo.conf:

[options]
admin_passwd = <a long random master password>
db_host = db
db_user = odoo
db_password = change-me-db
addons_path = /mnt/extra-addons
proxy_mode = True
list_db = False
workers = 4
max_cron_threads = 1
limit_memory_soft = 2147483648
limit_memory_hard = 2684354560
limit_time_cpu = 600
limit_time_real = 1200
  • admin_passwd (the master password) protects the database manager, where databases are created, duplicated, dropped and backed up. Make it strong.
  • list_db = False hides the database selector and manager from the public once you’ve created your database.
  • workers: with multiple workers, Odoo uses separate processes, which is needed for production performance. Rule of thumb: (CPU cores × 2) + 1, limited by RAM.
  • proxy_mode = True is required behind Nginx, so Odoo trusts the forwarded headers.
cd /opt/odoo && docker compose up -d

Nginx with HTTPS

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

server {
    server_name erp.example.com;
    client_max_body_size 100M;
    proxy_read_timeout 720s;

    location /websocket {
        proxy_pass http://odoochat;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    location / {
        proxy_pass http://odoo;
        proxy_set_header Host $host;
        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_redirect off;
    }
    gzip on;
    gzip_types text/css application/javascript application/json;
    # listen 443 ssl; certbot --nginx -d erp.example.com
}

(Older Odoo versions use /longpolling instead of /websocket.) See Nginx WebSocket proxy and Let’s Encrypt.

First setup

Open https://erp.example.com, create the database (company name, language, admin email and password), then install the apps you need: CRM, Sales, Invoicing, Inventory, Website… Configure outgoing and incoming mail servers (Settings → Technical) for notifications and email integration. See transactional email services.

Custom and community addons

Put modules (e.g. from the Odoo Community Association, OCA) into ./addons, restart, update the apps list, and install. Pin module versions to your Odoo version. Test on a staging copy before production. Duplicate the database from the database manager.

Backups

You need both the PostgreSQL database and the filestore (attachments in /var/lib/odoo/filestore/DBNAME):

  • the database manager’s backup (zip including the filestore), or
  • pg_dump plus a tar of the filestore, automated:
docker compose exec -T db pg_dump -U odoo -Fc mycompany > /backups/odoo-$(date +%F).dump
docker run --rm -v odoo_odoo-data:/data -v /backups:/b alpine tar czf /b/odoo-filestore-$(date +%F).tgz -C /data filestore

Copy them off-server, and test restores. See pg_dump and backup strategy.

Security

  • Strong master password, list_db = False, and HTTPS only.
  • Keep PostgreSQL internal. Enable 2FA for users (built into Odoo).
  • Apply Odoo security updates (pull the image with the same major tag), after a backup. See updating self-hosted apps.

Quick reference

Item Value
HTTP port 8069
Websocket port 8072 (/websocket)
Config odoo.conf (workers, proxy_mode, admin_passwd)
Addons /mnt/extra-addons
Filestore /var/lib/odoo/filestore

Related: ERPNext and PostgreSQL tuning.

  • Self-Hosting
  • Business
  • Docker