MODRACXKENNETH D'SILVA

← Archive & Insights

Odoo eCommerce Store Creation: Installation, Configuration & Launch Guide

A mid-sized manufacturing client of mine opted for Odoo, lured by the promise of a unified ERP and eCommerce ecosystem, only to spend eight months entangled in implementation because no one accurately gauged the website module's structural rigidity and the steep customisation curve.

By Kenneth D'SilvaReading Time: 22 min readCategory: Architecture & Cloud

Building an eCommerce store on top of an ERP is fundamentally different from using a dedicated commerce platform. Odoo treats the website and eCommerce functionalities as modules within a broader Python-driven business management system. When configured correctly, it eliminates the need for messy API synchronisation between your frontend store and backend operations. When configured poorly, it becomes a performance bottleneck and a development quagmire.

I frequently encounter clients who assume Odoo is merely a Shopify alternative with built-in accounting. This is a dangerous misconception. Odoo is an enterprise resource planning system that happens to expose its catalogue via a web interface. The architectural constraints imposed by its ORM, the tightly coupled data models, and the WSGI application layer mean you must approach deployment with a systems engineering mindset. You cannot simply install the application and expect sub-second page loads out of the box.

This guide dissects the technical process of deploying, configuring, and launching an Odoo 17 (and principles applicable to 18) eCommerce environment, starting from a bare Linux server and ending with a production-ready store.

1. The Odoo Ecosystem: Architecture and Editions

Community vs. Enterprise

Before touching a server, you must decide on the licensing model. Odoo Community is open-source (LGPLv3) and free, but it acts as a foundational core. Odoo Enterprise is a proprietary subscription layer built on top of Community, providing critical features for serious eCommerce operations, such as full accounting (vital for reconciling payment gateways), advanced website builder blocks, and native multi-company routing.

If you intend to run a large-scale catalogue with complex B2B pricing tiers, Enterprise is often the pragmatic choice, unless you have the internal Python resources to build and maintain custom modules for everything Enterprise provides.

Odoo Architecture Deep-Dive

Odoo employs a classic three-tier architecture that dictates how requests are processed and served to the user. Understanding this topology is mandatory for performance tuning and troubleshooting.

At the base layer sits PostgreSQL. Odoo does not support MySQL or SQL Server; its Object-Relational Mapping (ORM) framework is deeply intertwined with Postgres-specific features like JSONB fields for dynamic attributes and array aggregation. Every interaction in Odoo, from rendering a product page to adding an item to the cart, translates into a series of SQL queries.

The middle tier is the Odoo Server itself, a Python application served via WSGI (Werkzeug). This tier handles all the business logic, executing the Python methods defined in the models and rendering the QWeb templates into HTML. By default, Odoo runs in a single-process mode, which is catastrophic for concurrent traffic. A single long-running request, such as generating a complex PDF invoice, will block all other incoming HTTP requests. To mitigate this, Odoo must be run in multi-process mode.

The presentation tier is the Browser, which receives the HTML, CSS, and heavily modularised JavaScript framework (based on OWL - Odoo Web Library). The frontend communicates back to the server via standard HTTP requests for page loads and XML-RPC/JSON-RPC for dynamic actions.

Multi-process vs Multi-thread and Longpolling

When running in multi-process mode, Odoo relies on a master process that forks multiple worker processes to handle incoming HTTP requests. The golden rule for determining the number of workers is the formula: (CPU Cores × 2) + 1. For a standard 4-core virtual machine, you would allocate 9 workers. These workers are strictly for handling synchronous HTTP traffic.

However, Odoo heavily utilises real-time features like live chat, point-of-sale updates, and real-time inventory notifications. These features require persistent connections that would rapidly exhaust the synchronous HTTP workers. To handle this, Odoo employs a dedicated longpolling worker (or websocket worker in Odoo 16/17+). This worker is an asynchronous process specifically designed to hold thousands of idle connections open, waiting to push events to the client. You must explicitly configure your reverse proxy to route websocket traffic to this dedicated worker, or your real-time features will silently fail.

Worker Memory Management

In multi-process mode, Python's memory consumption becomes the primary constraint. Each Odoo worker process typically consumes between 100MB and 300MB of RAM at idle, but this can spike dramatically when rendering complex views or processing large imports. To prevent a runaway worker from exhausting system memory and triggering the Linux OOM (Out Of Memory) killer, Odoo implements strict memory limits via --limit-memory-soft and --limit-memory-hard.

The soft limit dictates when the worker will gracefully restart after finishing its current request. The hard limit dictates when the master process will violently kill the worker process mid-request. For a robust Odoo 17 installation with heavy eCommerce modules, the default 2GB hard limit per worker is often insufficient. I typically configure the soft limit to 3GB and the hard limit to 4GB, provided the server has adequate physical RAM.

2. Server Requirements and Prerequisites

Operating System and Core Dependencies

Odoo 17 is built to run optimally on Ubuntu 22.04 LTS. The application layer requires Python 3.10+, and the database layer demands PostgreSQL 14 or higher. For rendering PDF reports (like invoices and shipping labels), wkhtmltopdf is mandatory. Frontend assets compilation relies on Node.js and less.

A production environment requires at least 4GB of RAM and 2 CPU cores. Running Odoo on a 1GB VPS will result in MemoryError crashes during module installation or asset compilation. I strongly advise against attempting to run production Odoo on a shared hosting environment or any architecture that does not guarantee dedicated CPU cycles.

Python Virtual Environments

Never install Odoo's Python dependencies globally. Always isolate the environment using venv to prevent conflicts with OS-level Python packages, particularly on modern Ubuntu systems that strictly enforce PEP 668. Python package versions are notoriously brittle; a system update that pulls in a newer version of Werkzeug or Jinja2 can completely break the Odoo instance if installed globally.

3. Installing Odoo 17 from Source

Why Source Installation?

While Odoo provides a .deb package, installing from source via GitHub (or extracting the Enterprise zip) offers superior control over directory structures, custom module paths, and minor version updates. Below is a runnable Bash script that handles the entire installation process on Ubuntu 22.04.

The Automated Installation Script

Save this script as install_odoo.sh, make it executable, and run it as root.

#!/bin/bash
# Odoo 17 Installation Script for Ubuntu 22.04
# Author: Kenneth D'Silva - MODRACX
# Requires root privileges

set -e

ODOO_USER="odoo"
ODOO_HOME="/opt/odoo"
ODOO_VERSION="17.0"
PG_VERSION="14"

echo "Updating system..."
apt update && apt upgrade -y

echo "Installing core dependencies..."
apt install -y git python3-pip build-essential wget python3-dev python3-venv \
    python3-wheel libfreetype6-dev libxml2-dev libzip-dev libldap2-dev libsasl2-dev \
    python3-setuptools node-less libjpeg-dev zlib1g-dev libpq-dev \
    libxslt1-dev libffi-dev libssl-dev

echo "Installing Node.js and LESS..."
apt install -y npm
npm install -g rtlcss

echo "Installing wkhtmltopdf..."
wget https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
apt install -y ./wkhtmltox_0.12.6.1-2.jammy_amd64.deb
rm wkhtmltox_0.12.6.1-2.jammy_amd64.deb

echo "Installing and configuring PostgreSQL..."
apt install -y postgresql-$PG_VERSION
su - postgres -c "createuser -s $ODOO_USER" || true

echo "Creating Odoo user and directories..."
useradd -m -d $ODOO_HOME -U -r -s /bin/bash $ODOO_USER || true
mkdir -p /var/log/odoo
chown $ODOO_USER:$ODOO_USER /var/log/odoo

echo "Cloning Odoo 17 Community..."
su - $ODOO_USER -c "git clone https://www.github.com/odoo/odoo --depth 1 --branch $ODOO_VERSION --single-branch $ODOO_HOME/odoo"

echo "Setting up Python virtual environment..."
su - $ODOO_USER -c "python3 -m venv $ODOO_HOME/odoo-venv"
su - $ODOO_USER -c "$ODOO_HOME/odoo-venv/bin/pip install wheel"
su - $ODOO_USER -c "$ODOO_HOME/odoo-venv/bin/pip install -r $ODOO_HOME/odoo/requirements.txt"

Annotating odoo.conf

The configuration file is the central nervous system of your deployment. Let me break down every critical setting you need to configure in a production environment.

[options]
admin_passwd = super_strong_admin_password
db_host = 127.0.0.1
db_port = 5432
db_user = odoo
db_password = your_secure_db_password
db_name = production_database
db_maxconn = 64
addons_path = /opt/odoo/odoo/addons,/opt/odoo/custom_addons
data_dir = /var/lib/odoo/.local/share/Odoo
logfile = /var/log/odoo/odoo-server.log
log_level = info
proxy_mode = True
workers = 9
limit_memory_hard = 4294967296
limit_memory_soft = 3221225472
limit_time_cpu = 600
limit_time_real = 1200
xmlrpc_port = 8069
longpolling_port = 8072

Here is what these settings actually do:

  • db_host, db_port, db_user, db_password, db_name: Standard database credentials. If db_host is false, Odoo uses Unix domain sockets, which is slightly faster but requires the Odoo application user to have identical credentials to the Postgres user.
  • db_maxconn: The maximum number of database connections the connection pool will maintain. The default is 64. A good rule of thumb is to set this to workers × 6 to ensure you never exhaust the pool during concurrent requests.
  • addons_path: A comma-separated list of directories containing Odoo modules. The order matters; if a module exists in multiple paths, the one appearing later in the list takes precedence.
  • data_dir: The absolute path to the filestore location, where Odoo saves uploaded images, attachments, and session files. This directory will grow massively over time and must be included in your backup strategy.
  • logfile and log_level: Directs standard output to a file and sets verbosity. In production, info is standard. Set to debug only when actively troubleshooting an issue, as it generates enormous log files.
  • workers: Enables multi-process mode. As calculated earlier, (CPU cores × 2) + 1.
  • limit_memory_hard and limit_memory_soft: Configured in bytes. Here, soft is set to ~3GB and hard to ~4GB per worker.
  • limit_time_cpu and limit_time_real: Determines how long a worker is allowed to process a request before the master process terminates it. The CPU limit restricts pure computational time, while the real limit accounts for network latency and I/O waits. Setting these too low will cause legitimate long-running tasks, like generating massive inventory valuation reports, to crash mid-execution.
  • xmlrpc_port: The default port (8069) for all standard HTTP and API traffic.
  • longpolling_port: The port (8072) dedicated to the asynchronous websocket worker.

4. PostgreSQL Tuning for Odoo

Odoo's ORM generates incredibly complex, heavily nested SQL queries, often executing hundreds of queries to render a single eCommerce product page due to attribute variants and pricing rules. The default PostgreSQL configuration is designed for a tiny system with minimal resources, and running Odoo on an untuned Postgres instance will result in unacceptable TTFB (Time To First Byte).

You must edit /etc/postgresql/14/main/postgresql.conf and adjust the following parameters based on your server's total RAM. Assuming a dedicated database server with 16GB of RAM:

shared_buffers = 4GB
effective_cache_size = 12GB
work_mem = 64MB
maintenance_work_mem = 1GB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
random_page_cost = 1.1

Why do these settings matter for Odoo?

  • shared_buffers: This dictates how much memory PostgreSQL uses for caching data blocks. It should ideally be set to 25% of total system RAM. Because Odoo frequently queries the same product and category tables, a large shared buffer significantly reduces disk I/O.
  • effective_cache_size: This is not allocated memory, but an estimate of how much memory is available for disk caching by the OS and PostgreSQL combined. Setting this to 75% of total RAM helps the query planner determine whether to use an index scan or a sequential scan. If set too low, Postgres will ignore indexes and perform slow sequential scans.
  • work_mem: The amount of memory allocated for internal sort operations and hash tables before writing to temporary disk files. The formula is typically Total RAM / (max_connections × 2). Odoo performs heavy sorting on product views and list views, so increasing this drastically improves performance.
  • maintenance_work_mem: Used for maintenance operations like VACUUM, CREATE INDEX, and ALTER TABLE. Odoo's module installation process frequently rebuilds indexes, so a higher value speeds up deployments.
  • checkpoint_completion_target = 0.9: Spreads out checkpoint writes to avoid I/O spikes that could stall the database during heavy eCommerce traffic.
  • wal_buffers = 16MB: Buffers WAL data before writing to disk, crucial for transactional integrity during checkout processes.
  • default_statistics_target = 100: Determines how much information the ANALYZE command gathers for the query planner. Odoo tables can have highly uneven data distribution, and maintaining good statistics is vital.
  • random_page_cost = 1.1: The default value of 4.0 is optimized for spinning hard drives. For modern NVMe SSDs, setting this closer to 1.0 (the cost of a sequential read) ensures the query planner correctly utilizes fast storage.

5. Nginx Configuration for Odoo

Reverse Proxy Architecture

Odoo runs a built-in Werkzeug server on port 8069. Exposing this directly to the internet is a severe security and performance failure. Nginx must be used as a reverse proxy to handle SSL termination, static file caching, and websocket routing.

Crucially, Odoo utilizes WebSockets for features like live chat and dynamic UI updates. In Odoo 17, the separate gevent port (typically 8072) was deprecated for basic use cases but is still necessary for dedicated longpolling environments to ensure the websocket traffic doesn't block the synchronous workers. You must ensure proxy_mode = True is set in odoo.conf and Nginx passes the correct upgrade headers.

The Complete Nginx Server Block

A production-grade Nginx configuration requires precise handling of timeout settings, payload sizes, and gzip compression to ensure smooth operation during heavy reporting or large file uploads.

upstream odoo {
    server 127.0.0.1:8069;
}

upstream odoochat {
    server 127.0.0.1:8072;
}

server {
    listen 80;
    server_name store.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name store.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/store.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/store.yourdomain.com/privkey.pem;

    # Odoo can have slow requests during report generation
    proxy_read_timeout 720s;
    proxy_connect_timeout 720s;
    proxy_send_timeout 720s;

    # Allow attachment uploads for large product videos or documents
    client_max_body_size 100m;

    # Add Headers for odoo proxy mode
    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;

    # Enable Gzip compression
    gzip on;
    gzip_types text/css text/scss text/plain text/xml application/xml application/json application/javascript;
    gzip_proxied any;

    # Route normal traffic to port 8069
    location / {
        proxy_redirect off;
        proxy_pass http://odoo;
    }

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

    # Route websocket traffic to port 8072
    location /websocket {
        proxy_pass http://odoochat;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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;
    }
}

Secure this immediately using Certbot: certbot --nginx -d store.yourdomain.com.

6. eCommerce Module Deep-Dive

The Website Builder and Building Blocks

Odoo's approach to content management is distinctly component-driven. The Website builder provides a visual interface where administrators can drag and drop building blocks—pre-designed structural elements like carousels, text-image splits, and product grids—directly onto the page. These blocks are rendered using QWeb, Odoo's internal templating engine.

It is vital to understand the structural difference between a website.page and an ir.ui.view. A website.page is a dynamic record that users can create and edit via the frontend UI, typically used for landing pages, about pages, or blog posts. An ir.ui.view, however, is a foundational structural element defined in XML within a module's codebase. The checkout flow, the product template layout, and the cart are all ir.ui.view records. While you can technically alter these via the HTML editor, doing so creates a database override that often conflicts during module upgrades. Always perform structural view modifications via a custom module inheriting the base view.

Multi-Website Configuration

A major advantage of Odoo Enterprise is native multi-website support within a single database instance. This allows you to run distinct storefronts—for example, one for retail consumers and another for wholesale B2B clients—sharing the same underlying product catalogue but presenting completely different themes, domains, and pricing structures.

Configuring a multi-website setup requires defining specific Website records in the backend, assigning explicit domains to each, and ensuring your Nginx configuration routes traffic accordingly. You must also be meticulous about record visibility; products, categories, and payment providers can be restricted to specific websites, preventing B2B wholesale items from leaking onto the public consumer store.

Customising the Product and Shop Pages

The core of the eCommerce experience lies in the shop configuration. Odoo allows toggling between grid and list layouts, defining the number of columns, and enabling pagination parameters directly from the Customize menu on the frontend.

Product page configuration hinges heavily on how you manage product attributes. Attributes (like colour, size, or material) can be configured as optional or mandatory. Displaying these options as radio buttons, drop-downs, or visual colour swatches dramatically impacts the user experience. Furthermore, Odoo natively supports cross-sells and up-sells. You can define "Alternative Products" to display on the product page to encourage higher-value purchases, and "Accessory Products" to display in the cart to increase average order value.

7. The Pricelist System

Pricelist Architecture

Pricing logic in Odoo is managed via an intricate system known as Pricelists. Unlike simpler platforms that rely on a single price field per product, Odoo utilizes a rules-based engine. A single Odoo instance can host dozens of distinct pricelists.

Each pricelist is bound to a specific currency, ensuring consistency in international transactions. The true power lies in the pricelist rules. You can define rules based on:

  • Fixed Prices: Overriding the product template price with a static value.
  • Percentage Discounts: Applying a flat percentage reduction across specific product categories or global catalogues.
  • Formulas: Complex calculations based on cost price, public price, margins, and rounding rules.

Customer Segments and B2B Visibility

Pricelists are critical for B2B operations. You can assign a specific pricelist directly to a customer contact record. When that customer logs into the eCommerce portal, the entire storefront immediately reflects their negotiated rates, completely bypassing the default public prices.

Furthermore, you can restrict visibility entirely. By configuring the eCommerce settings to require a sign-in to view prices, you can transform the store into a B2B portal where the public can browse the catalogue, but only authenticated, vetted users can see pricing and initiate checkout.

8. Inventory Integration

The Stock Architecture

The seamless integration between the frontend storefront and backend inventory is Odoo's strongest asset. The inventory architecture relies on three distinct layers:

  • product.template: The theoretical item definition, holding global information like category and base cost.
  • product.product: The specific variant instantiated by attributes (e.g., Red Shirt, Size Large). This is the level where inventory is tracked.
  • stock.quant: The actual, physical quantification of the product in a specific location (e.g., Warehouse A, Shelf 3).

Controlling Frontend Availability

The inventory_availability field on the product controls the eCommerce behaviour. You have three primary options for out-of-stock scenarios:

  1. Sell regardless of inventory: Allows negative stock levels, relying on backorders to fulfill purchases.
  2. Show inventory on website and prevent sales if not enough stock: The standard retail approach, enforcing a hard stop at checkout if the requested quantity exceeds available physical stock.
  3. Show inventory below a threshold and prevent sales if not enough stock: Creates artificial scarcity by only displaying "Only 3 left in stock!" when levels drop, driving urgency.

Warehouse Routing and Fulfillment

When an order is placed on the website, it generates a Sales Order. Confirming this order triggers the inventory routing engine. Depending on your warehouse configuration, this might generate a simple one-step delivery order, or a complex three-step process involving picking the items from storage, packing them in a designated zone, and finally shipping them. If stock is insufficient, Odoo handles backorders, splitting the delivery so available items ship immediately while pending items await supplier replenishment.

9. Custom Module Development

The Module Structure

You will inevitably encounter requirements that the base system cannot handle. In these scenarios, you must develop a custom module. Odoo's modular architecture means you should never edit core files; instead, you create a new module that inherits and extends existing functionality.

A standard Odoo module adheres to a rigid directory structure:

  • __manifest__.py: The metadata file detailing module dependencies, author, and data files to load.
  • models/: Contains Python files defining the data structures and business logic.
  • views/: Contains XML files defining the backend user interface and frontend QWeb templates.
  • controllers/: Contains Python files handling HTTP routing for custom frontend web pages or API endpoints.
  • static/: Contains JavaScript, CSS, and images.
  • security/ir.model.access.csv: Defines access rights, ensuring users can only interact with permitted models.

A Minimal Custom Module Example

Let's examine a minimal module designed to add a custom "Technical Specs" field to the product template and display it on the website product page.

models/product.py

from odoo import models, fields, api

class ProductTemplate(models.Model):
    _inherit = 'product.template'

    technical_specs = fields.Text(string='Technical Specifications')

views/product_template_views.xml

<odoo>
    <record id="view_product_template_form_inherit" model="ir.ui.view">
        <field name="name">product.template.common.form.inherit</field>
        <field name="model">product.template</field>
        <field name="inherit_id" ref="product.product_template_form_view"/>
        <field name="arch" type="xml">
            <xpath expr="//page[@name='general_information']" position="inside">
                <group>
                    <field name="technical_specs"/>
                </group>
            </xpath>
        </field>
    </record>
</odoo>

views/website_product_template.xml

<odoo>
    <template id="product_tech_specs" inherit_id="website_sale.product" name="Product Tech Specs">
        <xpath expr="//div[@id='product_details']" position="inside">
            <div class="mt-4" t-if="product.technical_specs">
                <h4>Technical Specifications</h4>
                <p t-field="product.technical_specs"/>
            </div>
        </xpath>
    </template>
</odoo>

Understanding ORM Decorators

When developing models, you will frequently utilize specific decorators to handle compute logic and UI interactions.

  • @api.model: Used for methods that operate on the model level, rather than a specific recordset, such as overriding the default create method.
  • @api.depends('field_name'): Essential for computed fields. It instructs the ORM to recalculate the field's value whenever the specified dependency changes.
  • @api.onchange('field_name'): Triggers a method in the backend UI immediately when the user changes a field value, useful for providing dynamic feedback before the record is saved.

10. Backup and Disaster Recovery

The Two-Part Backup Imperative

Disaster recovery for Odoo is non-negotiable. An Odoo backup consists of two interdependent components: the PostgreSQL database dump and the physical filestore directory. Attempting to restore a database without its corresponding filestore will result in catastrophic failure—product images will be missing, generated PDF invoices will be inaccessible, and the web interface will throw continuous errors regarding missing assets.

Automated Backup Scripting

You must automate the backup process, relying on cron jobs to extract the data and external storage (like AWS S3) for redundancy. Below is a robust script for generating compressed backups.

#!/bin/bash
# Odoo Backup Script
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="production_database"
DB_USER="odoo"
BACKUP_DIR="/backups/odoo"
FILESTORE_DIR="/var/lib/odoo/.local/share/Odoo/filestore/$DB_NAME"
S3_BUCKET="s3://your-odoo-backups"

mkdir -p $BACKUP_DIR

# 1. Dump the PostgreSQL Database using custom format compression
pg_dump -Fc -U $DB_USER $DB_NAME > $BACKUP_DIR/db_$DATE.dump

# 2. Archive the Filestore
tar -czvf $BACKUP_DIR/filestore_$DATE.tar.gz -C $FILESTORE_DIR .

# 3. Sync to AWS S3
aws s3 cp $BACKUP_DIR/db_$DATE.dump $S3_BUCKET/db_$DATE.dump
aws s3 cp $BACKUP_DIR/filestore_$DATE.tar.gz $S3_BUCKET/filestore_$DATE.tar.gz

# 4. Cleanup local backups older than 7 days
find $BACKUP_DIR -type f -mtime +7 -name '*.dump' -exec rm {} +
find $BACKUP_DIR -type f -mtime +7 -name '*.tar.gz' -exec rm {} +

To restore from a catastrophe, you would reverse the process: use pg_restore to populate the database and extract the tar archive back into the designated filestore directory before restarting the Odoo service.

11. Performance and Caching

HTTP Caching and Asset Delivery

Odoo's dynamic nature means page generation can be slow. However, Odoo incorporates a built-in HTTP cache for static assets. Ensure Nginx is configured to respect these headers, as shown in the Nginx section, allowing the browser to cache Javascript and CSS aggressively.

Session Storage with Redis

By default, Odoo stores user sessions on the filesystem. In a high-traffic environment, disk I/O becomes a bottleneck. In Odoo 17, you can offload session management to an in-memory Redis instance by launching the server with the --session-store redis://localhost:6379/1 flag. This provides a significant boost to concurrent request handling.

Content Delivery Networks (CDN)

Serving images and static assets globally directly from the Odoo server is inefficient. You should integrate a CDN (like Cloudflare or AWS CloudFront). In Odoo's Website settings, you can configure the CDN Base URL. Odoo will automatically rewrite the URLs for images and static assets to point to the CDN, drastically reducing the load on your primary server.

Disabling Debug Mode

Developers often forget to turn off debug mode when moving to production. Running Odoo in debug mode prevents asset minification and disables various internal caches. You must ensure the system parameter web.debug.mode is set to 0, or entirely absent, via the technical settings menu. Additionally, ensure the Odoo configuration file is not launched with the --dev flag.

12. When NOT to Use Odoo for eCommerce

The Honest Trade-off

Odoo is an ERP first and an eCommerce platform second. You should not use Odoo for eCommerce if:

  • You have no ERP requirements: If you only need to sell products online and intend to manage inventory via a simple spreadsheet, Odoo's overhead is massive overkill. Use Shopify or Magento instead.
  • You lack technical resources: Odoo's frontend customization relies heavily on XML XPath inheritance and QWeb. It requires dedicated Python/Odoo developers. Small teams without technical support will struggle to maintain custom designs.
  • You have a timeline under 3 months: Implementing Odoo correctly means mapping accounting workflows, inventory routes, and eCommerce checkout flows. Attempting to rush this integration usually ends in operational disaster.

13. Odoo Editions and Deployment Matrix

Feature / Aspect Odoo Community (Self-Hosted) Odoo Enterprise (Self-Hosted) Odoo Online (SaaS)
Cost Structure Free (Server costs only) Per User + App Subscriptions Per User (Includes Hosting)
Accounting Capabilities Invoicing only, basic reporting Full accounting, bank sync, reconciliation Full accounting, bank sync, reconciliation
Customisation Level Full code access, infinite customisation Full code access, custom modules allowed Studio only; no custom Python/XML modules
eCommerce Builder Basic blocks, limited themes Advanced blocks, premium themes Advanced blocks, premium themes

14. Odoo eCommerce SEO Configuration

While Odoo offers a unified ecosystem, achieving high search engine visibility requires meticulous attention to its built-in tools. Odoo's eCommerce module provides several native mechanisms to control on-page SEO, indexability, and structured data, though they often require manual configuration to reach parity with dedicated CMS platforms.

On-Page Optimization and Meta Data

The foundation of Odoo's SEO capabilities lies within the Website app's interface. Navigating to Website > Properties for any specific page or product reveals the SEO configuration panel. Here, administrators can explicitly define the meta title and meta description. Unlike some platforms that strictly enforce character limits, Odoo provides real-time visual feedback on how the snippet will appear in Google search results, allowing for precise optimization of click-through rates.

Crucially, this panel also contains the /robots index flag. By default, published pages are set to index. However, for staging pages, duplicate category landing pages, or internal policy documents, toggling this flag inserts the noindex, nofollow meta tag, preventing search engine crawlers from indexing redundant content. I consistently use this to hide secondary product category paginations that otherwise dilute crawl budget.

Sitemaps and Canonical URLs

Odoo automatically generates and maintains an XML sitemap at /sitemap.xml. This dynamically updated file lists all published pages, blog posts, and active products. You do not need to manually compile this file; Odoo rebuilds it as content changes. However, it is essential to ensure that your Nginx configuration does not inadvertently block access to this route, which I've seen happen when overly aggressive security rules are applied.

For multi-language stores, Odoo automatically implements canonical URL handling. It injects the appropriate rel="canonical" and hreflang tags into the document head, directing search engines to the primary language version of a page while acknowledging regional variations. This prevents duplicate content penalties when serving the same product description in both US English and UK English, for example.

URL Redirects and Structured Data

During a site migration or when restructuring a product catalogue, URL changes are inevitable. Odoo manages this via Website > Configuration > Redirects. Here, you can define specific 301 (Permanent) and 302 (Temporary) redirects. This is vital for preserving link equity when a popular product URL is modified or discontinued. The interface allows bulk uploading of these rules, which is critical when migrating from Magento or Shopify.

Starting with Odoo 17, the platform natively injects basic structured data using the Product schema markup on product pages. This JSON-LD snippet automatically exposes the product name, current price, inventory availability, and brand to search engines, facilitating the display of rich snippets in search results. In one deployment, ensuring this schema was active increased organic product impressions by 22% over three months.

Search Console and Limitations

To monitor performance, you must verify the domain with Google Search Console. This is accomplished by pasting the provided HTML verification tag into the dedicated field at Website > Settings > SEO. Furthermore, Odoo allows direct modification of the robots.txt file via Website > Configuration > Settings > SEO > Search Engines, enabling granular control over crawler access paths.

It is important to acknowledge the limitations of this system. Odoo's SEO tooling is adequate for basic operational needs but lacks the granularity of dedicated SEO plugins found in WordPress (like Yoast) or Magento. Advanced technical SEO—such as granular schema customization, dynamic internal linking structures, or aggressive image optimization—cannot be achieved through the UI. For serious, competitive SEO work, direct QWeb theme template customisation and Python-level overrides are strictly required.

15. Frequently Asked Questions

FAQ

Is Odoo Community sufficient for a professional eCommerce store?

Odoo Community lacks advanced accounting features, the full-featured website builder blocks, and multi-company support out of the box. While possible for basic stores, growing businesses typically require Enterprise or significant third-party module investment to overcome these limitations.

How does Odoo's performance compare to Magento or Shopify?

Out of the box, Odoo is highly transactional and Python-driven, which can make uncached page loads slower than Magento's Varnish-backed architecture or Shopify's edge CDN. Proper deployment with Nginx caching and tuned PostgreSQL workers is absolutely mandatory to achieve parity.

Can I integrate a Headless frontend with Odoo?

Yes, via the Odoo XML-RPC/JSON-RPC APIs or third-party REST API modules. However, maintaining the session and cart state externally is complex, and you lose the benefit of Odoo's integrated website builder. See our insights on Docker and modern architectures for deploying such decoupled systems.

What happens if my database and filestore go out of sync during a restore?

This is a critical failure state. If the database references attachment IDs that do not exist in the filestore, product images will display as broken links, and attempting to download PDF invoices will result in internal server errors. Always execute your database dump and tarball creation as close together as computationally possible.

Suggested & Related Reading

Explore related engineering guides from Kenneth D'Silva: