Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Operations

How-to guide

Tenant lifecycle

Provision a tenant database, upgrade every tenant to a new release, suspend and reactivate a membership, and offboard a tenant on a multi-tenant node.

On a multi-tenant node every customer company is a tenant with its own database, registered in the control plane. This guide covers the operator commands that create a tenant, upgrade all tenants, change a membership, and end a tenant: server/provision_tenant.py, server/upgrade_tenants.py, server/membership.py, and server/offboard_tenant.py. The tenancy model itself is explained in multi-tenancy; self-service signup is in signup.

Prerequisites

  • A node with CLUSTER_ERP_MULTI_TENANT=true and a complete environment file (src/cluster_erp/config/settings.py:82). See configuration reference.
  • A shell in the checkout as the service user, with the environment file loaded, as install.sh runs these commands (deploy/install.sh:63-66, deploy/install.sh:215-220).
  • For --region: a region code listed in CLUSTER_ERP_REGIONS (src/cluster_erp/core/tenancy.py:456-457).

The states a tenant passes through are listed in TENANT_STATUSES: provisioning, active, suspended, and closed (src/cluster_erp/core/tenancy.py:48-50). The diagram shows the commands that move a tenant between them.

stateDiagram-v2
  [*] --> provisioning: provision_tenant or signup
  provisioning --> active: provision completes
  active --> suspended: membership sweep or suspend
  suspended --> active: membership extend or activate
  active --> closed: offboard_tenant
  suspended --> closed: offboard_tenant
  closed --> [*]: offboard_tenant --drop

In prose: a tenant is registered as provisioning, becomes active when provisioning completes, moves to suspended when its paid period ends or an operator suspends it, returns to active on extension or activation, becomes closed on offboarding, and loses its database and files with the --drop step.

Steps

  1. Provision a tenant. Source: src/cluster_erp/server/provision_tenant.py:3-5, src/cluster_erp/server/provision_tenant.py:163-172.

bash python -m cluster_erp.server.provision_tenant --key <key> --name "<company name>" --domains <domain>,<domain> --region <region>

The key must match TENANT_KEY_RE (src/cluster_erp/core/tenancy.py:46, src/cluster_erp/core/tenancy.py:460-461). The command registers the tenant as provisioning, creates its database on the region's server, replays the Alembic history, runs the real application start-up once against the new database (tables, sequences, permissions, first administrator), and marks the tenant active (src/cluster_erp/server/provision_tenant.py:85-157). Each step is idempotent, so a failed run resumes when you run it again (src/cluster_erp/server/provision_tenant.py:7-15). Self-service signup calls the same function after the domain is verified (src/cluster_erp/server/signup.py:472-478, src/cluster_erp/server/signup.py:546).

  1. List what a release would change, before every upgrade. Source: src/cluster_erp/server/upgrade_tenants.py:4, src/cluster_erp/server/upgrade_tenants.py:15-16.

bash python -m cluster_erp.server.upgrade_tenants --dry-run

  1. Take a backup of every database, then upgrade all tenants. install.sh runs the dry run, the backup, and the upgrade in this order on a multi-tenant node (deploy/install.sh:217-220). See backups and restore.

bash python -m cluster_erp.server.backup_tenants python -m cluster_erp.server.upgrade_tenants

The runner visits the default database and every tenant whose registration is not deleted, suspended tenants included. It migrates a database that is behind the head, then runs the one-shot start-up against every tenant to refresh its permission catalogue and seeds. A failing tenant is reported and skipped (src/cluster_erp/server/upgrade_tenants.py:62-115).

  1. If only one active tenant needs its seeds refreshed, run step 1 again with that tenant's key. An active tenant gets only the seed refresh (src/cluster_erp/server/provision_tenant.py:114-123). The operator console starts the same refresh with POST /api/v1/vendor/tenants/{key}/refresh-seeds (src/cluster_erp/server/routes/vendor.py:652-682).

  2. Extend, suspend, or reactivate a membership. Source: src/cluster_erp/server/membership.py:3-13, src/cluster_erp/server/membership.py:156-171.

bash python -m cluster_erp.server.membership extend --domain <domain> --days <days> python -m cluster_erp.server.membership suspend --domain <domain> python -m cluster_erp.server.membership activate --domain <domain>

extend moves the paid period forward and sets membership and tenant to active (src/cluster_erp/server/membership.py:83-106). Expired memberships are suspended automatically at start-up, daily, and by the cron sweep (src/cluster_erp/core/tenancy.py:324-345). See scheduled jobs and retention.

  1. Offboard a tenant: export its database and close the registration. Source: src/cluster_erp/server/offboard_tenant.py:3-9.

bash python -m cluster_erp.server.offboard_tenant --key <key> --note "<reason>"

The command writes an encrypted dump of the tenant database into backup_dir, then sets the tenant to closed, cancels its memberships, and stops sign-in resolution (src/cluster_erp/server/offboard_tenant.py:113-119, src/cluster_erp/core/tenancy.py:550-575).

  1. If the tenant's files and database must be removed, run the command again with --drop and the key typed a second time. Source: src/cluster_erp/server/offboard_tenant.py:4, src/cluster_erp/server/offboard_tenant.py:11-16.

bash python -m cluster_erp.server.offboard_tenant --key <key> --note "<reason>" --drop --confirm <key>

The command removes the tenant's folder from local storage, ends the database's sessions, drops the database, and appends the result to the tenant's closing note (src/cluster_erp/server/offboard_tenant.py:128-142). Earlier dumps stay until their own prune window, and the erasure ledger stays (src/cluster_erp/server/offboard_tenant.py:14-16).

Verify the result

  • provision_tenant prints tenant=<key> status=active db=<db> domains=<domains> (src/cluster_erp/server/provision_tenant.py:186-189).
  • upgrade_tenants prints all tenant databases at head and exits with code 0 (src/cluster_erp/server/upgrade_tenants.py:128-132).
  • python -m cluster_erp.server.membership list prints each tenant with its status and period end (src/cluster_erp/server/membership.py:53-81).
  • The operator console lists tenants with status, region, and schema revision; see operator console.

Troubleshooting

Symptom Cause in code Fix
tenant key ... must match ... The key breaks TENANT_KEY_RE (src/cluster_erp/core/tenancy.py:458-459) Use lowercase letters, digits, and underscores, starting with a letter.
unknown region ... The region is not configured (src/cluster_erp/config/settings.py:917-923) Add the region to CLUSTER_ERP_REGIONS and its server to CLUSTER_ERP_REGION_DB_URLS.
FAILED: <n> tenant(s) did not upgrade At least one tenant raised during upgrade (src/cluster_erp/server/upgrade_tenants.py:110-112, src/cluster_erp/server/upgrade_tenants.py:129-131) Read the logged exception of that tenant and run the runner again.
offboarding is a multi-tenant operation; this node is single-tenant multi_tenant is off (src/cluster_erp/server/offboard_tenant.py:158-160) Run on a multi-tenant node.
--drop requires --confirm <key> (typed again); nothing was done The confirmation is missing or differs (src/cluster_erp/server/offboard_tenant.py:108-110) Repeat the key after --confirm.
storage backend ...: remove the tenant's objects with the provider's tooling The storage backend is not local (src/cluster_erp/server/offboard_tenant.py:65-73) Remove the objects outside this command.

Discrepancies

Terms introduced

Term Code identifier Definition Turkish term Source
tenant key TENANT_KEY_RE The stable lowercase identifier of a tenant, also used as the suffix of its database name. kiracı anahtarı src/cluster_erp/core/tenancy.py:46
seed refresh _init_tenant_schema A one-shot application start-up against a tenant database that refreshes permissions, sequences, and deactivation columns. tohum yenileme src/cluster_erp/server/provision_tenant.py:39-82
offboarding offboard_tenant Ending a tenant: export, close, and optionally drop. ayrılış src/cluster_erp/server/offboard_tenant.py:1-16

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13