# Resource limits Every instance carries `cpu` (cores), `memory` (MB) and `storage` (GB) fields on `saas.instance`, sourced from the customer's plan at creation time. How those numbers actually get enforced differs by backend. ## Kubernetes Set directly as pod resource `requests` **and** `limits` (identical values — no burstable headroom): ```python resources.requests = {"cpu": f"{cpu}", "memory": f"{memory_mb}Mi"} resources.limits = {"cpu": f"{cpu}", "memory": f"{memory_mb}Mi"} ``` Storage is a PVC sized in whole GiB (`resources.requests.storage = f"{int(storage_gb)}Gi"`) on the storage class in `K8S_FILESTORE_STORAGE_CLASS` (default `gp3`). The custom-addons PVC is separate from the filestore PVC and always 2Gi regardless of plan — addon code doesn't scale with the customer's storage tier. ## Docker Enforced at container creation with explicit `mem_limit=f"{memory_mb}m"` and `nano_cpus=int(cpu * 1_000_000_000)` — both **must** be set explicitly per container, since Docker doesn't inherit limits from anywhere else. :::{admonition} Why one-off containers matter for memory :class: note During initialization, the filestore check and `odoo -i base` each run in their own **throwaway** container (`_docker_run_oneoff`) rather than exec'ing into the persistent server. Running the init process *inside* the already-running server would mean two Odoo processes competing for the same `mem_limit` at once — which is exactly what was causing OOM kills (exit code `137`) on tighter plans. The persistent container is only created once `-i base` has already succeeded. ::: ## Diagnosing resource exhaustion - **Exit code `137`** from any Docker exec or one-off run is a strong OOM signal — the kernel killed the process outright rather than Odoo exiting cleanly. Confirm on the Docker host with `dmesg -T | grep -i oom` or `journalctl -k --since "-5 minutes" | grep -i oom` — `docker inspect` on the container itself won't show `OOMKilled: true` for this case, since it's the *exec'd child* that gets killed, not the container's PID1. - **Host-level pre-checks** — before Docker/Kubernetes provisioning even starts, `check_host_resources()` compares the requested CPU/memory/storage against `get_host_resources()` (reads `/proc/meminfo`, `os.cpu_count()`, and `os.statvfs()` on `FILESTORE_BASE_PATH`), reserving 256MB of memory and 2GB of disk as headroom. Provisioning fails fast with a clear "Insufficient server memory/CPU/storage" message rather than starting a container that's doomed to fail. - **Worker sizing** for Odoo's own worker processes follows the standard `(cores × 2) + 1` formula.