Create and Manage Tenants
A Tenant represents an organization or major division within your company and serves as the top-level organizational unit in PaletteAI.
A default Tenant is automatically created when you install PaletteAI with global.featureFlags.systemDefaultResources: true in your Helm values. For production workloads with multiple organizations or divisions that require separate GPU quota pools or isolated administrative boundaries, we recommend creating separate Tenants. For guidance on when to create multiple tenants, refer to When to Create Multiple Tenants.
PaletteAI automatically creates a tenant-<name> namespace for internal operations when you create a Tenant. You can optionally create your own user namespace to store shared Settings and secrets that Projects inherit. For details on the namespace model, refer to Automatic Resource Creation.
Limitations
- Tenant creation is not available in the PaletteAI user interface at this time. Tenants must be created using Kubernetes manifests and
kubectlcommands as described in this guide.
Prerequisites
-
kubectl installed and available in your
$PATH. -
The
KUBECONFIGenvironment variable set to the path of the PaletteAI hub clusterkubeconfigfile.export KUBECONFIG=<kubeconfig-location> -
Cluster-admin permissions to manage cluster-scoped Tenant resources.
Create Tenant
Create a Tenant to define organizational boundaries, set GPU quotas, and control access across multiple Projects.
Enablement
-
Create a directory for the Tenant manifests, and then navigate to it.
mkdir <tenant-name>
cd <tenant-name> -
(Optional) Create a user namespace for shared Settings and secrets. This namespace is where you store Settings resources that Projects inherit. If you skip this step and do not create a Tenant-level Settings resource, each Project under the Tenant must set its own
settingsRef. Refer to Settings for more information.-
Create the Tenant namespace. The label
palette.ai/tenant: <tenant-name>associates this namespace with your Tenant.cat << EOF > namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: <user-namespace>
labels:
palette.ai/tenant: <tenant-name>
EOF -
Create a shared Palette secret. This secret must reference a Palette integration.
cat << EOF > palette-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: <palette-secret-name>
namespace: <user-namespace>
type: Opaque
stringData:
palette: |
{
"apiKey": "<your-palette-api-key>",
"defaultProjectID": "<your-default-project-id>",
"hostUrl": "<your-palette-host-url>",
"tenant": "<your-palette-tenant-name>",
"skipSSLCertificateVerification": false
}
EOF -
Create a Settings resource in the user namespace that references the Palette secret.
cat << EOF > settings.yaml
apiVersion: spectrocloud.com/v1alpha1
kind: Settings
metadata:
name: <settings-name>
namespace: <user-namespace>
spec:
integrations:
palette:
name: <palette-secret-name>
namespace: <user-namespace>
EOF
-
-
Create the Tenant manifest.
For a complete list of parameters, refer to the Tenant resource spec.
cat << EOF > tenant.yaml
apiVersion: spectrocloud.com/v1alpha1
kind: Tenant
metadata:
name: <tenant-name>
spec:
displayName: '<tenant-display-name>'
tenantRoleMapping:
groups:
- '<tenant-admin-group-1>'
- '<tenant-admin-group-2>'
EOF -
Apply the applicable manifests. If you skipped creating a user namespace or Settings resource in step 2, omit those commands.
# Apply user namespace (if created)
kubectl apply --filename namespace.yaml
# Apply Settings resource (if created)
kubectl apply --filename settings.yaml
# Apply Tenant (always required)
kubectl apply --filename tenant.yamlExample output (with all resources)namespace/primary-dev created
settings.spectrocloud.com/dev-settings created
tenant.spectrocloud.com/primary-dev created
Validate
-
Verify namespaces were created. You should see the user namespace (if you created one) and the controller-created
tenant-<name>namespace.kubectl get namespaces | grep --extended-regexp '(primary-dev|tenant-primary-dev)'Example outputprimary-dev Active 5m
tenant-primary-dev Active 5mYou can also list user namespaces associated with a Tenant using labels.
kubectl get namespaces --selector palette.ai/tenant=<tenant-name>infoOnly the
tenant-<name>namespace is displayed if you did not create a user namespace. The Tenant will work, but Projects must specify their ownsettingsRef. -
Verify the Tenant exists and is ready. If you reference a Settings resource, the
SETTINGScolumn is populated.kubectl get tenantsExample outputNAME DISPLAYNAME READY SETTINGS NAMESPACE PROJECTS AGE
default Default Tenant true default default 1 21d
primary-dev Primary Dev true dev-settings primary-dev 0 5m -
Verify the Tenant configuration.
kubectl describe tenant <tenant-name>The following example shows
Display Name,Settings Ref, andTenant Role MappingunderSpec. It also shows the Tenant is ready and has0child Projects underStatus.In
Status.Conditions,TenantNamespaceCreatedrefers to the controller-createdtenant-<tenant-name>namespace.Example commandkubectl describe tenant primary-devExample outputName: primary-dev
Namespace:
Labels: <none>
Annotations: <none>
API Version: spectrocloud.com/v1alpha1
Kind: Tenant
Metadata:
Creation Timestamp: 2026-02-07T15:26:04Z
Finalizers: spectrocloud.com/tenant-finalizer
Generation: 1
Resource Version: 12345678
UID: abc-123-def-456
Spec:
Display Name: Primary Dev
Settings Ref:
Name: dev-settings
Namespace: primary-dev
Tenant Role Mapping:
Groups:
admin
sre
Status:
Child Project Count: 0
Conditions:
Last Transition Time: 2026-02-07T15:26:05Z
Message: Settings configured and ready
Reason: SettingsConfigured
Status: True
Type: SettingsConfigured
Last Transition Time: 2026-02-07T15:26:05Z
Message: Tenant namespace created successfully
Reason: TenantNamespaceCreated
Status: True
Type: TenantNamespaceCreated
Ready: true
Events: <none>
Modify Tenant
To track changes in version control, update your manifests and apply them with kubectl apply.
Enablement
-
Open the manifest you want to update and make the necessary changes.
Use the following table to identify what manifest to update for common changes. For the full parameter list, refer to the applicable Resource page.
Resource Modifications Additional Information Tenant Update displayName. UpdatetenantRoleMapping.groups. UpdategpuResources.Tenants and Projects - GPU Quotas Settings Create a Settings resource in your user namespace (recommended), and then update the Tenant settingsRef. To remove shared Settings, deletesettingsReffrom the Tenant spec.Settings The following example adds a tenant admin group named
operationsand sets GPU resource limits.vi tenant.yamlUpdated TenantapiVersion: spectrocloud.com/v1alpha1
kind: Tenant
metadata:
name: primary-dev
spec:
displayName: 'Primary Dev'
settingsRef:
name: dev-settings
namespace: primary-dev
tenantRoleMapping:
groups:
- 'admin'
- 'sre'
- 'operations'
gpuResources:
limits:
'NVIDIA-A100': 64
'NVIDIA-H100': 48
'Default': 24
requests:
'NVIDIA-A100': 8
'NVIDIA-H100': 8
'Default': 4 -
Save the file and apply the updated manifest.
kubectl apply --filename <manifest-location>Example commandkubectl apply --filename tenant.yaml
Validate
Verify the updates were applied to the resource.
kubectl describe tenant <tenant-name>
The following example shows the updated tenant admin group and GPU resource limits.
kubectl describe tenant primary-dev
Name: primary-dev
# ... metadata omitted for readability
Spec:
Display Name: Primary Dev
Gpu Resources:
Limits:
Default: 24
NVIDIA-A100: 64
NVIDIA-H100: 48
Requests:
Default: 4
NVIDIA-A100: 8
NVIDIA-H100: 8
Settings Ref:
Name: dev-settings
Namespace: primary-dev
Tenant Role Mapping:
Groups: admin
sre
operations
Status:
Ready: true
Events: <none>
Delete Tenant
Delete a Tenant when you no longer need it. PaletteAI allows you to delete a Tenant only when it has no child Projects.
PaletteAI prevents you from deleting a Tenant that has child Projects. You must delete all Projects under the Tenant before you can delete the Tenant.
Enablement
-
Verify the Tenant has no child Projects.
kubectl get tenant <tenant-name> --output jsonpath='{.status.childProjectCount}'The output must be
0. If the value is greater than zero, delete the Projects under the Tenant before you continue.# List all Projects for this Tenant
kubectl get projects --all-namespaces --selector palette.ai/tenant-name=<tenant-name>
# Delete each Project
kubectl delete project <project-name> --namespace <project-namespace> -
Delete the Tenant.
kubectl delete tenant <tenant-name>Example commandkubectl delete tenant primary-dev -
(Optional) Delete the user namespace and Tenant namespace.
PaletteAI does not delete namespaces automatically when you delete the Tenant. Namespaces may contain Settings and secrets that Projects still reference.
Before deleting namespaces:
-
Confirm there are no child Projects:
kubectl get tenant <tenant-name> --output jsonpath='{.status.childProjectCount}' -
Confirm no remaining Projects reference the shared Settings namespace (if used).
-
Confirm you no longer need any Secrets/Settings stored in the user namespace.
# Delete user namespace (if you created one)
kubectl delete namespace <user-namespace>
# Delete controller-created Tenant namespace
kubectl delete namespace tenant-<tenant-name>Examplekubectl delete namespace primary-dev
kubectl delete namespace tenant-primary-dev -
Validate
Verify the Tenant no longer exists.
kubectl get tenants
NAME DISPLAYNAME READY SETTINGS NAMESPACE PROJECTS AGE
default Default Tenant true default default 1 21d
Next Steps
After you create a Tenant, create Projects under the Tenant. Each Project inherits the Tenant Settings (if configured) and GPU quotas. PaletteAI grants Tenant admin groups admin permissions in all Project namespaces.
If you encounter issues when creating or managing Tenants, refer to Troubleshooting Tenants.