Administrator troubleshooting

Operational failure patterns in saas.provisioner, for diagnosing a stuck or failed instance from the platform side (see Provisioning issues 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 — 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.

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/<addon> 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 — Git ADD is not Odoo INSTALL, by design.