ERP load is not flat. Accounting closes the month in the last three days, sales pulls quarter-end reports, and inventory counts happen on Saturdays. A server sized for those days is idle for the rest, and a server sized for the rest is a support ticket at month-end. This post follows a DevOps engineer solving that for Odoo on MassiveGRID PaaS, which is built on Virtuozzo Application Platform, formerly Jelastic, where the resources follow the calendar.

The company is a services firm we will call Lindqvist & Rowe, about 120 Odoo users on the Odoo Community Edition package. The complaint is specific: from the 28th to the 2nd, invoice validation takes seconds instead of milliseconds and the aged-receivables report times out.

Finding out what is actually slow

The engineer starts with the platform's statistics for both containers, which record RAM, CPU, disk I/O and network per minute. The picture over the previous month-end is clear. The Odoo container hits its cloudlet ceiling on CPU for hours at a time while accounting validates hundreds of invoices, and the PostgreSQL container's CPU and IOPS spike whenever a large report runs, dragging the transactional work with it. Neither container is short of memory. The company has been raising RAM to fix a CPU problem.

Fix one: let the containers grow when the month ends

A cloudlet is 128 MiB of RAM plus 400 MHz of CPU, and the platform's automatic vertical scaling allocates cloudlets to a container as its load rises, up to the limit you set, then releases them. The engineer raises the Odoo node's dynamic limit from 24 to 64 cloudlets and PostgreSQL's from 16 to 40. Nothing changes on a normal Tuesday: the containers use what they used before. On the 28th they expand, and the hourly bill for those days reflects it. Billing is per hour on the greater of RAM or CPU actually consumed, so the 27 quiet days cost what they always did.

She also sets a load alert at 85% of the new limits for 15 minutes. If the alert fires at month-end, the ceiling is still too low; if it never fires, the ceiling is right.

Fix two: give Odoo the workers to use the CPU

More CPU does nothing if Odoo is not configured to use it. Odoo's multi-process mode runs a fixed number of HTTP workers set by the workers option, plus cron workers, and the usual guidance is about one worker per two CPU cores plus one, each with memory limits. At the old 24-cloudlet ceiling (roughly 9.6 GHz of CPU) the instance ran 4 workers; at 64 cloudlets it can run 12. The engineer sets workers = 12, max_cron_threads = 2, and raises the per-worker soft memory limit so that heavy report requests are not recycled mid-run. Because vertical scaling supplies the CPU only when the workers are busy, running 12 workers costs nothing extra on quiet days: idle workers use almost no CPU and a modest amount of RAM.

SettingBeforeAfterWhy
Odoo dynamic cloudlet limit2464Month-end CPU ceiling
workers412Use the available cores during peaks
limit_memory_soft per workerDefaultRaised for report-heavy workersAvoid recycling during large reports
PostgreSQL dynamic cloudlet limit1640Report and validation concurrency
Load alertsDefault85% for 15 min on both nodesVerify the new ceilings

Fix three: move the reports off the transactional database

The report timeouts had a second cause. A long analytical query on the same PostgreSQL that is validating invoices competes for the same cache and I/O. The engineer adds a PostgreSQL Primary-Secondary Cluster in the same environment group: the primary takes Odoo's writes, and one hot-standby replica, kept current by streaming replication, takes read-only work. Odoo itself does not split reads and writes, so the replica is used by what actually caused the pain: the finance team's BI dashboards and the scheduled exports, which are pointed at the standby's hostname. Month-end reporting then reads from a node that has no invoices to validate.

The cluster's Pgpool-II layer, if enabled, can also balance read-only statements across standbys for tools that support it. Lindqvist & Rowe kept it simple: writes and Odoo to the primary, analytics to the replica.

Fix four: the Saturday inventory count

Inventory counts on Saturdays used to be slow because a scheduled action, the platform's cron running Odoo's scheduled jobs, was recomputing stock valuation at the same time. The engineer moved the heavy scheduled actions to Sunday night in Odoo's scheduler and set the cron worker count so that a long job does not block the others. It is not a platform feature, but the platform's per-minute statistics are what made the overlap visible.

Result and cost

The next month-end passed without a ticket. Invoice validation stayed under a second with 40 accountants working; the aged-receivables report ran from the replica in under a minute. The cost difference was three days of higher cloudlet consumption plus the replica node, which at MassiveGRID's published $0.003372 per cloudlet-hour added on the order of $20 to $30 to the month before discounts. A server sized permanently for the peak would have cost that every day.

Frequently Asked Questions

Should I scale Odoo horizontally instead, with several application nodes?

For most companies vertical scaling is the better first step: a single Odoo node with enough workers handles hundreds of users, and it avoids the shared-filestore and session considerations of multiple nodes. Horizontal scaling suits thousands of concurrent users or strict availability needs; the platform supports it with a load balancer and shared storage for the filestore.

Does the PostgreSQL replica help Odoo itself, or only external reporting?

Odoo sends all its queries to one database connection string, so the replica does not offload Odoo's own reports by itself. It offloads everything you point at it: BI tools, exports, backups (take dumps from the standby) and custom read-only integrations. That alone usually removes the contention that slows Odoo.

How do I know the right cloudlet limit?

Set it generously, watch the per-minute statistics through one full month-end, and use the load alert as the signal. The limit is a ceiling, not a reservation, so a limit that is higher than needed costs nothing until the load actually arrives.

Resources that follow the calendar

Run Odoo Community Edition on MassiveGRID PaaS with vertical auto-scaling for month-end, a PostgreSQL replica for reporting and per-cloudlet-hour billing that drops when the books are closed. Free 14-day trial, no credit card.

Odoo Community on MassiveGRID PaaS

Further Reading