# Administrator troubleshooting Operational failure patterns in `saas.provisioner`, for diagnosing a stuck or failed instance from the platform side (see [Provisioning issues](../troubleshooting/provisioning.md) for the customer-facing version of this). ## Provisioning failed, instance stuck in "error" Every provisioning failure is caught, logged with `_logger.exception`, and triggers an automatic rollback (`_rollback_failed_provision` / `_rollback_failed_provision_docker`) before the instance is written to `state: error`. Rollback deletes the Deployment/Service/Ingress (Kubernetes) or removes the container (Docker) — but **never** touches the tenant's database or filestore/addons volumes, so retrying provisioning after fixing the underlying issue doesn't lose data. Check `instance.error_message` first — it's the raw exception string, and `instance.log_activity()` records a matching "ERP Provisioning Failed" activity entry. ## Exit code 137 during initialization See [Resource limits § Diagnosing resource exhaustion](resource-limits.md) — this is almost always an OOM kill. On Docker, the rollback path logs the container's `OOMKilled`/`ExitCode` state *before* removing it, specifically so this is diagnosable after the fact. ## `LockNotAvailable: could not obtain lock on row in relation "ir_cron"` This means `--max-cron-threads=0` (`disable_cron=True`) wasn't in effect during initial boot — the persistent server's own cron thread and the separate `odoo -i base` exec were both touching `ir_cron` rows concurrently. On Kubernetes this is controlled by `create_kubernetes_deployment(..., disable_cron=True)` during the first boot, then redeployed with cron re-enabled after `initialize_odoo()` succeeds. On Docker, `initialize_odoo_docker()` avoids this entirely by running `-i base` in a one-off container *before* the persistent container exists at all — if you see this error on the Docker backend, something is creating the persistent container too early. ## Kubernetes deployment stuck "still terminating after 60s" Deployment deletion (before recreating) waits up to 60 seconds for the old Deployment to actually disappear before giving up and attempting to create the replacement anyway, retrying the create itself up to 5 times on `409` conflicts. If you see this warning repeatedly for the same tenant, check for a stuck finalizer on the Deployment rather than assuming the provisioner is at fault. ## Metrics missing or all zero - **Kubernetes** — requires `metrics-server` installed in the cluster; `_collect_metrics_kubernetes` logs and continues (doesn't fail the whole run) if the `metrics.k8s.io` API call fails for a given pod. - **Docker** — reads `container.stats()`; CPU percent is computed from a single instantaneous delta rather than a rolling average, so a very idle or very bursty container can look artificially low/high on any single poll. - Both backends still record database size (via `pg_database_size`) and filestore size (via `du`, run inside a throwaway/exec context — never read directly off the host filesystem from the platform container) even if live CPU/memory metrics fail. ## Cloudflare DNS record exists but doesn't verify See [Domains § Troubleshooting](domains.md). ## Addon deployment succeeded but the module doesn't appear Deploying an addon via Git (`deploy_addon_to_instance`) only gets the code into `/mnt/extra-addons/` and restarts the tenant — it deliberately does **not** call `install_custom_module[_docker]()`. The customer still has to trigger "Update Apps List" and then explicitly install the module from Apps. See [Architecture § Custom addons](../developer/architecture.md) — Git ADD is not Odoo INSTALL, by design.