tenant
Manage tenants within projects for multi-tenant isolation.
Synopsis
pfy tenant <subcommand> [flags]Aliases: tn
Description
Tenants provide multi-tenant isolation within a project. Each tenant has separate data, configuration, and applications while sharing the same project infrastructure.
Subcommands
list- List all tenants in a projectcreate- Create a new tenantget- Get tenant detailsupdate- Update a tenant's name, description and/or deployment modedelete- Delete a tenant (prompts for confirmation; requires--yeswith--ci)
tenant list
List all tenants within a specific project.
Usage
pfy tenant list --project <project-slug> [flags]Flags
| Flag | Required | Description |
|---|---|---|
--project | Yes | Project slug to list tenants from |
Example
pfy tenant list --project my-saasOutput:
Tenants:
- Development (dev)
- Staging (staging)
- Production (prod)Verbose output:
pfy --verbose tenant list --project my-saasConnecting to ProductifyFW manager...
Fetching tenants for project 'my-saas'...
Found 3 tenant(s)
Tenants:
- Development (dev)
ID: 11111111-2222-3333-4444-555555555555
- Staging (staging)
ID: 22222222-3333-4444-5555-666666666666
- Production (prod)
ID: 33333333-4444-5555-6666-777777777777tenant create
Create a new tenant within a project.
Usage
pfy tenant create \
--project-id <id> \
--slug <slug> \
--name <name> \
[--description <desc>]Flags
| Flag | Required | Description |
|---|---|---|
--project-id | Yes | Project ID (not slug) |
--slug | Yes | Tenant slug (unique within project) |
--name | Yes | Tenant display name |
--description | No | Tenant description |
Examples
Basic creation:
pfy tenant create \
--project-id 01234567-89ab-cdef-0123-456789abcdef \
--slug dev \
--name "Development"With description:
pfy tenant create \
--project-id 01234567-89ab-cdef-0123-456789abcdef \
--slug prod \
--name "Production" \
--description "Live production environment with customer data"Output:
Tenant created successfully: Production (prod)Verbose output:
pfy --verbose tenant create \
--project-id 01234567-89ab-cdef-0123-456789abcdef \
--slug prod \
--name "Production" \
--description "Live production environment"Connecting to ProductifyFW manager...
Creating tenant 'prod' in project ID '01234567-89ab-cdef-0123-456789abcdef'...
Tenant created successfully: Production (prod)
ID: 33333333-4444-5555-6666-777777777777tenant update
Update a tenant's name, description and/or deployment mode.
Usage
pfy tenant update <slug|id> --project <project-slug> [flags]Flags
| Flag | Required | Description |
|---|---|---|
--project | No* | Project slug — needed to resolve a tenant slug to its ID |
--name | No | New tenant display name |
--description | No | New tenant description |
--deployment-mode | No | shared or dedicated — the tenant's deployment topology |
--confirm | No | Required with --deployment-mode shared: un-dedicating deletes deployment rows |
* Not needed when the tenant is addressed by ID.
At least one of --name, --description or --deployment-mode must be given.
Deployment mode
--deployment-mode switches a tenant between the shared topology (one backend per application serves every tenant; the tenant is resolved per request from the caller's signed cookie) and the dedicated topology (the tenant gets its own deployment, upstream app--tenant, and hostname app.tenant.<base>, on which the tenant is pinned by the rendered proxy config).
See Shared vs. dedicated tenants for when to choose which, and for the DNS requirements — including the one-label wildcard caveat, which is the single most common way a dedication is left half finished.
Dedicating:
pfy tenant update acme --project retail --deployment-mode dedicatedTenant acme deployment mode set to dedicated
DNS checklist (2 records):
- operations.acme.apps.example.com CNAME proxy.example.com
- analytics.acme.apps.example.com CNAME proxy.example.com
The tenant's hostnames will not resolve until these records exist.The DNS checklist is the actionable half of the operation — nothing provisions DNS for you, and the tenant's hostnames stay dark until the records exist. Note that a *.apps.example.com wildcard does not cover operations.acme.apps.example.com: a wildcard matches exactly one label, and a dedicated hostname has one more.
Un-dedicating (deletes the tenant's dedicated deployments, so --confirm is mandatory):
pfy tenant update acme --project retail --deployment-mode shared --confirmTenant acme deployment mode set to shared
No DNS changes required.Without --confirm the CLI refuses before touching the network:
switching to "shared" deletes this tenant's dedicated deployments; re-run with --confirmData migration is out of scope
Switching modes moves routing, not data. Dedicating seeds deployment specs; un-dedicating deletes them. Neither reads or writes your application's data — moving it between a dedicated backend and the shared one is an application concern. Decide it before flipping the mode.
Combined with other fields — one invocation, both applied:
pfy tenant update acme --project retail \
--name "Acme Corporation" \
--deployment-mode dedicatedAuthorization
Changing deployment mode requires system administrator, or the maintainer or owner role on the owning project. Bare project membership is not enough: the project roles are readonly, tester, developer, maintainer and owner, and un-dedicating permanently deletes every dedicated deployment spec. That is a destructive action, so it sits at the same bar as pfy deployment create and pfy deployment delete rather than below them.
A delegated tenant administrator cannot dedicate their own tenant even holding tenant-lifecycle permission — deployment topology is an infrastructure and cost decision with DNS consequences. Every change is audited.
Targeting a dedicated deployment
Once a tenant is dedicated, an application has more than one deployment per environment: the shared row, plus one per dedicated tenant. Every spec-consuming command therefore accepts --tenant to say which one:
pfy deployment get --env prod # the SHARED deployment
pfy deployment get --env prod --tenant acme # acme's DEDICATED deployment
pfy deployment set --env prod --tenant acme --field resources.memory_mb=8192
pfy deployment snapshot --env prod --tenant acme # -> deploy/spec.prod.acme.yaml
pfy deployment apply deploy/spec.prod.acme.yaml
pfy nomad render --env prod --tenant acme --tag v1.2.3| Flag | Meaning |
|---|---|
| (omitted) | the shared deployment — pre-existing behaviour, unchanged |
--tenant <slug> | that tenant's dedicated deployment |
--deployment-id <id> | a specific row; combined with --tenant the CLI verifies they agree instead of silently picking |
Two behaviours worth knowing:
- An
--envalias inproject.yamlcaches the shared deployment it was created against, so--tenantdeliberately bypasses that cache — otherwise it would be silently ignored. - The upstream (
operations--acme) is server-derived. The CLI never computes it, so the job, group, service and task names inherit it with no second implementation to drift.
Without --tenant, a command that resolves by --env/--app alone against a dedicated application reports the ambiguity rather than guessing.
GraphQL Operations
GetTenants
query GetTenants(
$projectSlug: String!
$filters: [FilterInput!]
$order: [OrderInput!]
$pagination: PaginationInput
) {
tenants(
projectSlug: $projectSlug
filters: $filters
order: $order
pagination: $pagination
) {
id
slug
name
}
}CreateTenant
mutation CreateTenant($projectId: ID!, $input: CreateTenantInput!) {
createTenant(projectId: $projectId, input: $input) {
id
slug
name
}
}Common Workflows
Create environment-based tenants
# Get project ID
PROJECT_ID=$(pfy project get --slug my-project | grep "ID:" | awk '{print $2}')
# Create dev tenant
pfy tenant create \
--project-id $PROJECT_ID \
--slug dev \
--name "Development" \
--description "Development and testing"
# Create staging tenant
pfy tenant create \
--project-id $PROJECT_ID \
--slug staging \
--name "Staging" \
--description "Pre-production validation"
# Create prod tenant
pfy tenant create \
--project-id $PROJECT_ID \
--slug prod \
--name "Production" \
--description "Live customer environment"Create customer-based tenants
PROJECT_ID="01234567-89ab-cdef-0123-456789abcdef"
# Create tenant for customer A
pfy tenant create \
--project-id $PROJECT_ID \
--slug customer-a \
--name "Customer A Corp" \
--description "SaaS instance for Customer A"
# Create tenant for customer B
pfy tenant create \
--project-id $PROJECT_ID \
--slug customer-b \
--name "Customer B Inc" \
--description "SaaS instance for Customer B"List tenants and create application
# List tenants to find the right one
pfy tenant list --project my-project
# Get tenant ID from the list
TENANT_ID="33333333-4444-5555-6666-777777777777"
# Create application in that tenant
pfy application create \
--tenant-id $TENANT_ID \
--slug webapp \
--name "Web Application" \
--version "1.0.0"Best Practices
- Environment-based tenants - Use
dev,staging,prodfor environments - Customer-based tenants - Use customer identifiers for SaaS isolation
- Consistent slugs - Use lowercase, hyphen-separated slugs
- Descriptive names - Include purpose in descriptions
- Plan hierarchy - Design tenant structure before creation
Multi-tenant Strategies
Environment Isolation
# Separate environments within same project
pfy tenant create --project-id $ID --slug dev --name "Development"
pfy tenant create --project-id $ID --slug qa --name "QA"
pfy tenant create --project-id $ID --slug prod --name "Production"Customer Isolation
# Separate customers in SaaS application
pfy tenant create --project-id $ID --slug acme-corp --name "Acme Corp"
pfy tenant create --project-id $ID --slug tech-startup --name "Tech Startup Inc"Geographic Isolation
# Region-based tenants
pfy tenant create --project-id $ID --slug us-east --name "US East"
pfy tenant create --project-id $ID --slug eu-west --name "EU West"
pfy tenant create --project-id $ID --slug asia-pac --name "Asia Pacific"Error Handling
Tenant already exists
Error: failed to create tenant: tenant with slug 'dev' already exists in projectSolution: Choose a different slug or update the existing tenant.
Project not found
Error: failed to create tenant: project not foundSolution: Verify the project ID using pfy project get.
Invalid tenant slug
Error: failed to create tenant: invalid slug formatSolution: Use lowercase letters, numbers, and hyphens only.
See Also
- project - Manage projects
- Commands - All available commands
- Manager Multi-tenancy Concepts