Why self-host Odoo Community
The SaaS version of Odoo, sold via Odoo.sh or through partners, charges per user and per activated module. For a 10-person SMB using five modules, the monthly bill quickly exceeds several hundred euros. A VPS starting at 99 DH/month is enough to run the same instance, with no restrictions on the number of users or Community modules.
Data control is the other compelling argument. With a self-hosted instance, the PostgreSQL database stays on your server, in your datacenter of choice. No third party accesses your customers, quotes, invoices or HR data. For companies subject to compliance requirements — healthcare, legal, fintech — this control is not optional.
Finally, customisation is unrestricted. Odoo Community allows installation of any module from the community or developed in-house, without restrictions from the publisher. On a SaaS or managed instance, third-party modules often need to be validated, and some customisations are simply impossible.
What Odoo 17 Community includes
- CRM: sales pipeline, leads, activities and customer tracking
- Sales: quotes, purchase orders, pricing and payment terms
- Accounting: chart of accounts, journals, bank reconciliation, VAT
- Invoicing: customer and supplier invoices, automated reminders, payments
- Inventory: stock management, locations, replenishment rules
- Purchasing: purchase requests, tenders and supplier tracking
- Human Resources: employees, contracts, leave and attendance
- Email Marketing: mailing lists, campaigns and send statistics
- Website & e-commerce: page builder, integrated online store
- Helpdesk: customer ticketing, SLA and self-service portal
Differences between Community and Enterprise
Scroll the table
| Feature | Community | Enterprise | Open-source alternative |
|---|---|---|---|
| Advanced accounting (analytical reports) | Basic | Full | OCA `account-financial-reporting` module |
| Electronic signature | Not included | Included | Community `sign` module or Docusign |
| Advanced timesheets | Not included | Included | OCA `hr-attendance` module |
| Official editor support | Community forum | Guaranteed SLA | Certified Odoo partner |
| Automatic updates (Odoo.sh) | Not applicable | Included | Manual migration script |
| Number of users | No cap | License-based | No restrictions |
| Price (self-hosted) | Free | Free if self-hosted * | Free |
VPS requirements for hosting Odoo 17
Odoo is a Python/PostgreSQL application with significant RAM requirements, especially when multiple users work simultaneously. The minimum recommended sizing for an SMB with 5 to 15 users:
- CPU: 4 vCPU minimum; Odoo is multi-process and benefits from parallelism
- RAM: 8 GB recommended (4 GB is functional for testing, insufficient in production)
- Storage: 40 GB NVMe SSD; plan for more if you activate e-commerce with media
- OS: Ubuntu 22.04 LTS or Debian 12 — both are stable and well-documented
- Docker Engine 24+ and Docker Compose v2 (integrated plugin, docker compose without hyphen)
- A domain name pointing to the VPS IP, with an active A or CNAME record
- A reverse proxy (Caddy or Nginx) to handle TLS and expose Odoo on port 443
A 299 DH/month VPS from ServOrbit.com easily meets these requirements with dedicated resources.
Install Odoo 17 with Docker Compose
Create the directory structure
Connect via SSH to your VPS and create the working tree:
mkdir -p /opt/odoo/{data,addons,config,backups} cd /opt/odooThese folders will hold persistent data, custom modules, Odoo configuration and backups respectively.
Write the docker-compose.yml file
Create
/opt/odoo/docker-compose.ymlwith the following content:services: db: image: postgres:16 restart: unless-stopped environment: POSTGRES_DB: postgres POSTGRES_USER: odoo POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./data/postgres:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U odoo"] interval: 10s retries: 5 odoo: image: odoo:17.0 restart: unless-stopped depends_on: db: condition: service_healthy environment: HOST: db USER: odoo PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./data/odoo:/var/lib/odoo - ./addons:/mnt/extra-addons - ./config/odoo.conf:/etc/odoo/odoo.conf ports: - "127.0.0.1:8069:8069" - "127.0.0.1:8072:8072"Note that ports are only exposed on localhost — the reverse proxy handles external access.
Create the .env file and Odoo configuration
Create
/opt/odoo/.env:POSTGRES_PASSWORD=$(openssl rand -hex 32) echo "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}" > /opt/odoo/.envThen create
/opt/odoo/config/odoo.conf:[options] db_host = db db_port = 5432 db_user = odoo db_password = ${POSTGRES_PASSWORD} addons_path = /usr/lib/python3/dist-packages/odoo/addons,/mnt/extra-addons admin_passwd = REPLACE_WITH_A_STRONG_PASSWORD without_demo = all workers = 4 max_cron_threads = 2 limit_memory_hard = 2684354560 limit_memory_soft = 2147483648Configure the reverse proxy with Caddy
Caddy is the simplest choice: it handles TLS automatically via Let's Encrypt. Install it and create
/etc/caddy/Caddyfile:odoo.yourdomain.com { reverse_proxy 127.0.0.1:8069 # WebSocket support for real-time notifications handle /websocket { reverse_proxy 127.0.0.1:8072 } header { X-Frame-Options SAMEORIGIN X-Content-Type-Options nosniff Strict-Transport-Security "max-age=31536000; includeSubDomains" } encode gzip }Reload Caddy:
systemctl reload caddy.Start the containers
From
/opt/odoo, run:docker compose up -dFollow the startup logs:
docker compose logs -f odooOdoo takes 30 to 60 seconds to initialise. You will see
HTTP service (werkzeug) runningwhen it is ready.Access the interface and create the database
Open
https://odoo.yourdomain.com/web/database/managerin your browser. Enter the master password (admin_passwd) defined inodoo.conf, choose a database name, language and administrator email, then click Create. The first initialisation takes 2 to 5 minutes depending on the selected modules.
Configure the database and instance
After the database is created, Odoo redirects you to the main interface. The first thing to do is verify the master password in Settings > Technical > Master Password. This password protects access to the database manager — it must be long and unique.
In Settings > General, configure the company name, base currency, time zone and address. These settings influence generated documents (invoices, purchase orders).
If you are migrating from another system, Odoo provides CSV importers for customers, suppliers, products and accounting balances. Always start with a test environment using real data before switching to production.
Configure automated backups
A production Odoo instance without backups is an immediate technical debt. The recommended strategy relies on two levels.
Daily PostgreSQL dump with a cron script:
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=/opt/odoo/backups
DB_NAME=your_database
docker compose -f /opt/odoo/docker-compose.yml exec -T db \
pg_dump -U odoo ${DB_NAME} | gzip > ${BACKUP_DIR}/odoo_${DATE}.sql.gz
# Retain 7 days
find ${BACKUP_DIR} -name "odoo_*.sql.gz" -mtime +7 -deleteSchedule this script in /etc/cron.d/odoo-backup to run at 02:00 every night.
Volume backup: the /opt/odoo/data/odoo folder contains attachments, images and uploaded files. Synchronise it daily to object storage (S3, Scaleway Object Storage) using rclone or restic.
Activate key modules after installation
- Sales (
sale_management): quotes, orders, customer portal — core module for any commercial activity - Accounting (
account_accountant): chart of accounts, VAT, bank reconciliation, closings - Inventory (
stock): multi-warehouse management, lots and serial numbers, replenishment rules - Purchasing (
purchase): purchase requests, tenders, receiving and supplier invoicing - Human Resources (
hr): employee file, organisational structure, HR documents - Helpdesk (
helpdesk): ticketing, SLA, customer self-service portal — Community includes basic features - Website (
website): page builder, blog, contact forms - E-Commerce (
website_sale): online store, cart, payment — activated alongside the Website module
Optimise your instance performance
Adjust the number of workers
In
odoo.conf, theworkersparameter controls the number of Python processes handling HTTP requests. The practical rule:workers = (vCPU × 2) + 1. On a 4 vCPU VPS, setworkers = 4. Below 2, Odoo runs in single-process mode, which blocks long requests (imports, reports).Configure memory limits
Odoo kills workers that exceed defined limits to prevent memory leaks.
limit_memory_soft = 2147483648 # 2 GB: graceful end signal limit_memory_hard = 2684354560 # 2.5 GB: immediate killOn 8 GB RAM with 4 workers + PostgreSQL, these values are consistent. Increase them if your reports are slow or workers restart frequently (
docker compose logs odoo | grep killed).Limit crons to avoid CPU saturation
The
max_cron_threads = 2parameter reserves 2 threads for scheduled tasks (emails, replenishment, reports). Beyond that, crons compete with HTTP workers. On a server dedicated to Odoo, you can go up to 4, but start conservatively.Enable Redis session storage (optional)
By default, Odoo stores sessions on disk. On shared volumes or with frequent restarts, this can cause disconnections. Add a Redis service to
docker-compose.ymland configure:session_store = redis redis_url = redis://redis:6379This option is especially useful if you plan a multi-instance architecture in the future.
Secure the Odoo instance in production
The security of an Odoo instance relies on several complementary layers.
Disabling the database manager in production is the most important measure. This interface, accessible by default at /web/database/manager, allows anyone who knows the master password to create, delete or restore databases. Add to odoo.conf:
dbfilter = ^your_database$
list_db = Falselist_db = False hides the management interface and prevents database enumeration.
Force TLS: Caddy handles this natively. With Nginx, ensure the HTTP vhost systematically redirects to HTTPS and that Strict-Transport-Security is set.
Fail2ban: configure a jail for failed login attempts on /web/login. Odoo logs these attempts at warning level — a regex filter on Login failed is sufficient.
Strong master password: this password should have at least 32 randomly generated alphanumeric characters. It can only be changed by modifying odoo.conf and restarting the container — make it a vault password from the start.
Advanced tip: integrating custom modules
The /opt/odoo/addons folder is mounted in the container at /mnt/extra-addons. To install a community module (OCA, or developed in-house), place it in this folder and add the path to addons_path in odoo.conf.
After adding a module, restart Odoo (docker compose restart odoo), then go to Settings > Apps > Update Apps List. The module appears in the list and installs like any native module.
For OCA modules, clone directly from GitHub into the addons folder: the [OCA organisation on GitHub](https://github.com/OCA) groups hundreds of thematic modules (French accounting, EDI, e-commerce connectors, etc.) that are actively maintained.
Hosting comparison: self-hosted VPS vs Odoo SaaS
Scroll the table
| Criterion | Self-hosted VPS (ServOrbit.com) | Odoo.sh / SaaS partner |
|---|---|---|
| Monthly cost (10 users) | From 99 DH/month | €150 to €500 depending on modules |
| Data control | Total — data on your server | Data with publisher or partner |
| Community modules | All available, without restriction | Depends on plan and partner |
| Enterprise modules | Not included (separate licence) | Included in subscription |
| Backups | Your responsibility, configurable | Managed by platform |
| Updates | Manual (major migration) | Managed (assisted upgrade) |
| Customisation | No cap | Restricted or billable |
| Skills required | Linux administration, Docker | No server skills needed |
| Technical support | Community + partner of choice | SLA per publisher contract |