This is where the model stops being abstract. You've read the concepts and seen the YAML shapes; now you'll write a small, real piece of catalog and apply it.
We start with a cell because it's the smallest unit of fleet modeling that captures the thing Terraform couldn't do for you: placement. A cell answers "where should the next tenant go, and what's the limit?" — the fleet decision at the center of the cattle mindset. Get one cell working and you've internalized the heart of the model.
A prod-clusterscell that groups your production clusters, ranks them by load for placement, and caps the fleet. You'll need the cluster entity type it groups, so we define that first.
A cell groups entities of a single type, so before the cell we need the type. We'll use TenantCluster — a shared cluster that hosts many tenants. Put this in a file called blueprint.yaml:
The tenant-count metric is what makes placement meaningful — it's a live count of how loaded each cluster is. (It references a runs_on relationship type; you'll add that in Your First Fleet. For now, an empty fleet just reports zero, which is exactly what you want when placing the first tenant.)
Add the cell as a second document in the same file, separated by ---:
Read it as three decisions:
entityType: TenantCluster — the cell's members are clusters. A cell groups exactly one type.placementPolicy: least-loaded — when something needs a home, Terrantula sorts members by tenant-count ascending and offers the emptiest one. (round-robin and random are the alternatives.)constraints — a fleet-wide ceiling. aggregate: sum, max: 500 means the total tenant count across every cluster in the cell may not exceed 500. This is the capacity rule that used to live in a runbook nobody read — now Terrantula enforces it before any provisioning happens.From the directory holding blueprint.yaml:
Terrantula validates both documents, checks that the cell's entityType references a type that exists, and reconciles the model. You'll see the entity type and the cell created.
If you haven't pointed the CLI at a server, this applies to a local SQLite database on your machine — no signup, no infrastructure. Run terrantula dashboard afterward to see your TenantCluster type and prod-clusters cell in the fleet view. When you're ready to share with a team, terrantula login --api-url ...switches the same commands to a remote project.
A cell with no members can't place anything, so add a cluster. Cells use explicit membership by default — you add entities to them. Once you've created a TenantCluster entity (via import or an Action) and added it to prod-clusters, the cell has a member to rank.
You can confirm what the cell sees from the CLI:
With one empty cluster in the cell, a placement query (the kind an OnboardTenant Action issues) will return that cluster — its tenant-count is zero, so it's the least-loaded by definition. Add a second cluster and place a few tenants, and least-loaded starts doing real work: each new tenant lands on whichever cluster currently has the fewest, until the per-cluster and fleet-wide limits kick in.
That's the whole point of a cell. You declared where things go and how full is too full, once, in a few lines of YAML — and Terrantula now enforces it on every onboard, automatically, before anything touches your infrastructure.
tenant-count) and a policy (least-loaded).You've now modeled the central fleet decision. The next section turns this into a complete, working fleet: importing your existing Terraform state, defining the full entity types and cells, adding the runs_on relationship, and wiring the OnboardTenant Action that opens PRs.
Next: Your First Fleet → — the guided, end-to-end build of every primitive in order.