Self-service tenant provisioning that does not need an engineer
A product where onboarding a customer requires manual database work is not yet a product — it is a service with a login page. Provisioning is the boundary: everything a new tenant needs must be created automatically, atomically enough to be safe, and recoverably enough that a half-finished signup is not a support ticket.
The first few customers of any SaaS product get set up by hand, and that is fine. The problem is that manual provisioning scales linearly with customers while revenue per customer does not, and it quietly caps how fast the business can grow — not because demand is missing, but because someone has to run a script.
Everything a tenant actually needs
Provisioning is easy to under-scope because the obvious part — insert a row in the tenants table — is the smallest part. The full list is longer than it looks, and each missing item becomes a bug that only appears for brand-new customers.
The failure to design for is partial provisioning
Provisioning touches your database, your payment provider, possibly an email service and an object store. Those cannot participate in a single transaction. So the real question is not how to make provisioning atomic — it is what happens when step four of seven fails.
- Do all local database work inside one transaction, so the tenant either exists completely or not at all.
- Perform external calls after that transaction commits, driven by a durable record of work still to do.
- Make every external step idempotent, keyed on the tenant id, so a retry cannot create two billing customers.
- Track provisioning state on the tenant record itself, so a half-finished tenant is queryable rather than invisible.
- Let the tenant into the product as soon as the local state is complete — do not block first login on a billing API.
That last query is the whole point. Without an explicit state column, a tenant whose billing setup failed looks identical to a healthy one until the customer complains.
Idempotency across external systems
Every external call needs a key derived from the tenant, not from the attempt. Payment providers support this directly; for anything that does not, check-then-create guarded by a local record of what you already did.
Trials, plans and the entitlement boundary
Provisioning is where entitlements are first applied, and the design decision that matters is whether feature access is read from the plan or copied onto the tenant. Copying is tempting and wrong: a plan change then has to find and rewrite every copy.
- Store the tenant's plan; derive entitlements from the plan at read time
- Allow per-tenant overrides as an explicit, auditable exception, not as the normal mechanism
- Record trial end as a timestamp on the tenant, and treat expiry as a state transition with its own handling
- Decide what an expired trial can still do — read-only access and data export is usually kinder, and better for winbacks, than a hard lockout
The half nobody builds
Provisioning gets built because signups need it. Deprovisioning gets skipped because churn is unwelcome — and then a departing customer's deletion request arrives and there is no path for it.
The metric that tells you it works
One number: engineering minutes required to onboard one new tenant. If it is not zero, provisioning is not finished. Track it, and treat any manual intervention as a defect with a root cause rather than as routine operational work.
Questions
Should a new tenant be able to use the product before provisioning completes?
Yes, as soon as the local database state is complete. Blocking first login on an external billing call means a provider outage becomes a signup outage. Let them in once the tenant, admin user and defaults exist, and finish billing setup in the background — with monitoring that catches it if that background work fails.
How do you provision when each tenant gets its own schema or database?
The same sequence applies, with the schema or database creation as an additional step that must be idempotent and recorded. The important difference is that it is slower and more failure-prone, so it belongs in background work with explicit state rather than inside the signup request — and your migration tooling must then handle a tenant that exists but is mid-provision.
What is the most common provisioning bug?
Missing reference data. The tenant record, user and billing all succeed, so provisioning reports success, but the product requires a default category or template that was never seeded. The new customer's first session dead-ends and they conclude the product is broken — which, for them, it is.