Tuning an instance
Odoo's performance settings are a small set of numbers that interact badly when set independently: worker count, memory limits per worker, request time limits, and how many of those the machine can actually afford.
Antelao asks you for the workload instead: who the instance serves at the same time, counted per profile. The worker count, the limits and the container's memory ceiling are derived from that and from what the host has left.
Where it lives
On the instance's Configuration page, in two sections at the foot: Workload, where you
describe who the instance serves, and Tuning options, which shows what was calculated from it.
Both are collapsed until you open them, and each heading carries its answer - 12 light, 2 heavy, 30 portal; 1 cron thread(s) and Auto, 3 worker(s), 1 cron thread(s) - so the page says how the
instance is sized without being opened. The tab's one Save saves both.
Tuning is a configuration change like any other: saving it queues the apply. See Configuration and changes, which is where that page is pictured.
The workload
| Profile | Who | What one person's share costs a worker |
|---|---|---|
| Light users | Internal users on forms, lists and chatter | About 150 MiB (Odoo's estimate) |
| Heavy users | Reporting, large imports and exports, accounting and manufacturing runs | About 1 GiB (Odoo's estimate) |
| Portal visitors | Website, e-commerce and portal customers: short anonymous requests | About 100 MiB |
Plus the cron threads: each one is a process beside the workers that runs scheduled actions. One is enough until scheduled actions queue up.
A worker's expected memory is the weighted average over the counts, with one floor from Odoo's deployment guide: a fifth of all requests are heavy whatever the users (reports, exports and scheduled actions run on every instance), so a light-only workload is costed at 80 % light and 20 % heavy, about 325 MiB per worker, and the breakdown says so on its worker line.
The three memory figures are on the same card. The light and heavy ones are Odoo's own estimates; leave them unless the Statistics page shows the workers holding more. Reset to default puts the cron threads and the three figures back; the counts are facts about your customer and stay.
What is calculated
With the method on Auto:
- Workers: about one per six light or heavy users, one per twelve portal visitors, and never fewer than two, in Auto and Manual alike. Odoo renders a PDF by fetching the report's styles from its own web port, so a single worker waits on itself: an accounting report exported to PDF froze a one-worker instance until the request time limit ended it. The host's CPUs never hold them down; when the workers, the cron threads and the websocket worker are more processes than two per CPU plus one (Odoo's rule), a warning under Workers says that requests will queue on the CPU under load, and the health check reports it.
- Memory limits per worker: Odoo's own production values, the same on every host. They bound a worker's virtual memory; a worker that crosses the soft limit is recycled after its request.
- Limit real time: 1200 seconds - a request still running after that is stopped and its worker restarted, so a big import through Odoo's web client has twenty minutes, and a request that never finishes costs a worker just as long. An Auto tuning that still holds the former 120 seconds takes 1200 the next time it is saved. A Manual tuning keeps its own value, and an Auto tuning can override it: lower it when stuck requests take up the workers of a small instance. The value is between 60 and 3600 seconds: Odoo stops every worker at once at 0, so anything under the minimum is refused, and the Health tab reports a lower value that was stored earlier. The instance's web server timeouts follow this value by themselves, always 60 seconds longer (1260 seconds at the default), so Odoo ends a long request itself rather than the web server giving up on it first. Changing the value rewrites the site in the same apply.
- Container ceiling: what the workers, the cron threads and the websocket worker are expected to hold together, with a quarter on top. Each worker and each cron thread is counted at the expected worker figure, since the cron threads run mail, reports and scheduled imports. The websocket worker serves the bus and long-polling only, and is counted at 256 MiB, never more than a worker. Shown under Container limits as the derived limits.
- Database connections: from the number of processes.
Then the host is checked. What it has left for this instance is its memory minus a reserve for the system, the PostgreSQL cluster's ceiling, the management services as last measured, and what the other instances use (their ceiling only while there is no reading). Memory never lowers the workers: when they need more than the host has left, a warning under Workers says so with the figures, the container's ceiling is clamped to what the host can give, and the health check reports the instance and says what would change the outcome: more memory, or less taken by the others.
Manual keeps the figures you type. The recalculate button on the Tuning options heading fills them with what Auto would choose, as a starting point.
Auto recalculates only when you save. When what is stored differs from what Auto would store now
- an instance tuned under older rules, or a host that changed - a line under Workers says what a recalculation stores: press the recalculate button, then Save.
Overriding a calculated value
In the rare case where one figure has to differ from what Auto calculates, Auto keeps the rest without switching the whole instance to Manual. With the method on Auto, each figure on Tuning options has an Override box beside it. A non-zero value is used instead of the calculated one. Empty or 0 uses the calculated value.
| Figure | Accepted override | On the command line |
|---|---|---|
| Workers | 2 to 100 | --override-workers |
| Limit requests | 1024 to 16384 | --override-limit-request |
| Limit CPU time (s) | 600 to 1800 | --override-limit-cpu |
| Limit real time (s) | 60 to 3600 | --override-limit-real |
| Memory soft (bytes) | At least 629145600 (600 MiB) | --override-memory-soft |
| Memory hard (bytes) | At least 1677721600 (1600 MiB) | --override-memory-hard |
| Max DB connections | 10 to 256 | --override-connections |
An override is held to the same limits as the field it replaces, and is refused before anything is saved. Memory soft must not end up above Memory hard, whichever value applies to each.
- The figures follow. An override takes the place of its figure inside the calculation, so the CPU and memory warnings, the database connections and the derived limits describe what will run. A Workers override changes them; the page shows the effect as you type.
- Saving applies it like any tuning change, and an override of Limit real time moves the web server's timeouts with it.
- Clearing one gives the calculated value back. For Limit requests, Limit CPU time and Limit real time, that is the default: 8192 requests, 600 seconds and 1200 seconds.
- Manual ignores them. The boxes are shown in Auto only. Overrides stay stored while the method is Manual, so going back to Auto restores them.
- They travel with a backup and a clone of the instance. The heading of Tuning options ends
in
with overrideswhile any is set.
From the command line they are set with ohm instance quota set. Memory takes a size such as
2GiB, and 0 clears an override:
ohm instance quota set --instance fanes --override-workers 4 --override-memory-hard 2GiB --server pelmo
ohm instance quota set --instance fanes --override-workers 0 --server pelmo
The fit bars
The Workload card opens with two bars: Memory, the container ceiling this workload needs against what the host leaves this instance, and CPU, its processes against the two per CPU plus one Odoo serves well. Green is within, amber is tight (memory from 80 %, CPU from 100 %), red is over-committed (memory past 100 %, CPU past 150 %); the figure beside the bar keeps counting past 100 % when the bar is full. The Dashboard's Tuning fit card shows the same two bars for the whole host, with every instance's ceiling counted whether it runs or not, and a row per instance beneath.
Where the memory goes
Under the derived limits, Where the memory goes opens a table in two halves: what the container's ceiling is made of (the master, every worker at its expected figure, the cron threads, the websocket worker, the headroom) and what the host has and gives away (its reserve, PostgreSQL, the management services, every other instance by name) until what is left for this instance. A line that is a sum opens to what it is made of, down to the container holding the memory: the management services one by one, the PostgreSQL cluster and its pooler, each other instance with its ceiling beside what it uses, and this instance's ceiling with what its container holds right now. The refresh button on the line re-reads the host and recomputes the table and the verdict. The same table is under the Tuning finding on the Health tab, as the last check saw it.
Sizing against the host
Workers are per instance, memory is per host. Ten instances each sized for fifty users will oversubscribe a machine that could serve five hundred users as one instance - which is why the other instances' ceilings are part of what each one is sized against.
The Resources page shows host and per-container CPU and memory over time, which is the honest answer to whether the current sizing fits.