Complete guide to multi-tenant provisioning and management.
The Tenant controller provisions selected namespace resources:
- Isolated Kubernetes namespaces
- Per-tenant secrets for provider access
- RBAC with service accounts and roles
- Resource quotas and limits
- Tenant status fields for later operational integrations
Review the generated RBAC before production use. The tenant service account can list Secrets in its namespace.
apiVersion: agentic.clawdlinux.org/v1alpha1
kind: Tenant
metadata:
name: acme-corp
spec:
displayName: "ACME Corporation"
namespace: agentic-customer-acme
providers:
- cloudflare-workers-ai
- openai
quotas:
maxWorkloads: 100
maxConcurrent: 10
maxMonthlyTokens: 10000000
cpuLimit: "10"
memoryLimit: "20Gi"
slaTarget: 99.5
networkPolicy: trueApply:
kubectl apply -f tenant-acme.yamlkubectl get tenants
kubectl describe tenant acme-corp
kubectl get tenants acme-corp --watchWatch for:
- Phase: Pending → Provisioning → Active
- NamespaceCreated: true
- SecretsProvisioned: true
- RBACConfigured: true
- QuotasEnforced: true
Once tenant status is "Active", deploy workloads:
kubectl apply -f workload.yaml -n agentic-customer-acmeEach tenant gets exclusive namespace:
agentic-customer-acme (tenant: acme-corp)
agentic-customer-bigco (tenant: bigco)
agentic-customer-startup (tenant: startup)Service accounts limited to their namespace:
acme-corp-agent (can manage workloads in agentic-customer-acme only)
bigco-agent (can manage workloads in agentic-customer-bigco only)Secrets copied to tenant namespace:
agentic-system/cloudflare-workers-ai-token
↓
agentic-customer-acme/cloudflare-workers-ai-token (copy)The Tenant CRD exposes spec.networkPolicy, but the current tenant reconciler
does not create the following policy automatically. Treat this as an example:
kind: NetworkPolicy
metadata:
name: acme-isolation
namespace: agentic-customer-acme
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: agentic-customer-acme
egress:
- to:
- namespaceSelector: {}quotas:
maxWorkloads: 100 # Max concurrent AgentWorkloads
maxConcurrent: 10 # Max concurrent executions
maxMonthlyTokens: 10M # Max tokens per month
cpuLimit: "10" # CPU cores
memoryLimit: "20Gi" # RAMThe tenant reconciler creates a Kubernetes ResourceQuota:
maxWorkloadsmaps to a pod-count quota.cpuLimitmaps to total CPU quota.memoryLimitmaps to total memory quota.
maxConcurrent and maxMonthlyTokens are not enforced by this ResourceQuota.
View quota usage:
kubectl get resourcequota -n agentic-customer-acme
kubectl describe resourcequota acme-corp-quota -n agentic-customer-acmeTrack per-tenant costs:
kubectl get tenant acme-corp -o json | jq '.status.tokensUsedThisMonth'The current tenant reconciler does not populate that field. Use a durable
CostReporter and aggregation integration before relying on tenant chargeback.
kubectl delete tenant acme-corpThe current reconciler does not implement finalizer-based cleanup. Deleting a Tenant does not guarantee deletion of its namespace, RBAC, quotas, or Secrets. Inventory and remove retained resources explicitly.
- Use descriptive names -
acme-corp, nottenant1 - Set realistic quotas - Don't set limits too low
- Define SLA monitoring externally -
slaTargetis not a complete SLO system - Apply NetworkPolicy explicitly - Confirm the CNI enforces it
- Rotate secrets - Update provider tokens quarterly
For examples, see Examples.