Skip to content

Deploy one package across all tenant databases

You’ve got one product schema and a fleet of tenant databases that all need it. Deploy them one at a time and you’re doing the same job a hundred times, praying you don’t skip one. And the list is never still — a customer signs up this afternoon and now there’s a database your deploy script has never heard of.

Hey folks. I’m Forge Barrett, master of the Content Forge here at SchemaSmith.

This is fan-out on the database axis — one package, many databases of the same engine, reconciled in a single run. Course 2 taught the schema axis, many schemas inside one database. This is the other one: many whole databases, discovered live, forged the same. Let’s fire it up.

The template asks the catalog who’s in the fleet

Section titled “The template asks the catalog who’s in the fleet”

On a single database you name your target. On a fleet you don’t — you declare how to find it. That’s one line in the template. Here it is for SQL Server, straight out of Package/Templates/Main/Template.json:

"DatabaseIdentificationScript": "SELECT [Name] FROM sys.databases WHERE [Name] LIKE 'fleet[_]tenant[_]%'"

That query runs against the server’s own catalog and returns the roster. Every database named fleet_tenant_* is in the fleet; nothing else is. No side table, no config list — the catalog is the source of truth. Each engine keeps its own catalog, so only the query dialect changes:

SQL ServerPostgreSQLMySQL
Catalogsys.databasespg_databaseinformation_schema.schemata
Predicate[Name] LIKE 'fleet[_]tenant[_]%'datname LIKE 'fleet\_tenant\_%'SCHEMA_NAME LIKE 'fleet\_tenant\_%'

Same idea, three catalogs. (The underscores are escaped — [_] on SQL Server, \_ on the others — so _ matches a literal underscore, not any character. In the PostgreSQL and MySQL JSON that backslash is doubled: fleet\\_tenant\\_%.)

Before you deploy across anything, ask the tool who it would touch. --PreviewTargets reads the catalog and lists the fleet — read-only, no changes:

Terminal window
schemaquench --ConfigFile:quench.settings.json --PreviewTargets
Locate Databases To Quench (localhost,11433)
Template: Main [required]
db: fleet_tenant_001
db: fleet_tenant_002
db: fleet_tenant_003
db: fleet_tenant_004
db: fleet_tenant_005

Five tenants, discovered live. That’s your roster, confirmed before a single table is touched.

Now quench. One command:

Terminal window
schemaquench --ConfigFile:quench.settings.json

SchemaSmith turns the roster into work units — one per database — and runs them:

[localhost,11433].[fleet_tenant_001] Dispatching work unit (source: db=DatabaseIdentificationScript, schema=(regular template))
[localhost,11433].[fleet_tenant_002] Dispatching work unit (source: db=DatabaseIdentificationScript, schema=(regular template))
[localhost,11433].[fleet_tenant_003] Dispatching work unit (source: db=DatabaseIdentificationScript, schema=(regular template))
[localhost,11433].[fleet_tenant_004] Dispatching work unit (source: db=DatabaseIdentificationScript, schema=(regular template))
[localhost,11433].[fleet_tenant_005] Dispatching work unit (source: db=DatabaseIdentificationScript, schema=(regular template))

source: db=DatabaseIdentificationScript — every one of those targets came from the catalog, not a config file. Five work units, four tables each — Customer, Product, SalesOrder, OrderItem — the whole Shop schema struck into every tenant. One command, five databases, done.

Quench a second time. Nothing happens.

Terminal window
schemaquench --ConfigFile:quench.settings.json

SchemaSmith reconciles each tenant to the declared shape. A fleet already in shape is a five-way no-op — no drift, no double-apply, no surprise sparks. That’s the safety net that makes fan-out worth trusting: re-running the whole fleet is always safe, so a half-finished run just needs running again.

Here’s where the catalog roster earns its keep. A new customer signs up. Stand up their database:

CREATE DATABASE fleet_tenant_006;

Deploy again — and you edit nothing:

Terminal window
schemaquench --ConfigFile:quench.settings.json

Discovery returns six now. Only fleet_tenant_006 gets forged; the other five are no-ops. You onboarded a tenant with a CREATE DATABASE and a deploy you already run. No package edit, no release, no pull request. The roster followed the catalog, and the catalog followed reality.

When the catalog shouldn’t own the roster

Section titled “When the catalog shouldn’t own the roster”

Discovery-by-convention is the right default, but not the only answer. Sometimes the deployment system should own the list — you want to hit a named set of tenants from config, or roll out to one region at a time, or narrow a run to a handful for a canary. That’s Module 2, where TemplateTargets hands the roster to your config and lets you steer it. For now, the catalog is your roster, and that’s plenty.

Check yourself: After you CREATE DATABASE fleet_tenant_006 and re-deploy, why is fleet_tenant_006 the only database that changes?

Because the deploy is idempotent: SchemaSmith reconciles every discovered tenant to the declared Shop shape. The existing five are already in shape, so they’re no-ops; only the brand-new, empty fleet_tenant_006 needs the schema forged into it. Discovery found all six from the catalog — but only the one that differs from the declaration gets any work.


A smith with one true mold doesn’t care whether the order is for five blades or fifty. He runs the batch, every piece off the same form, and any blank that’s already true just passes clean through. That’s your fleet — one package, struck into every tenant the catalog names, and the ones already shaped come off the fire untouched.

Running a fleet where you’re not sure every tenant matches? Email me at forgebarrett@schemasmith.com — tell me how you find your databases today, and I’ll tell you whether the catalog can just do it for you.

Next up: Course 7 · Module 2 — Steering the roster, where the list of tenants comes from your deployment config instead of the catalog — named sets, regions, and a safe canary subset.

Until then, may your roster read true, your fleet quench clean, and every tenant you onboard fall in line with a single strike.

— Forge