Install MaaS Components
Complete Operator Setup before proceeding.
Installation flow:
- Database Setup — Create the PostgreSQL connection Secret
- Create Gateway — Deploy maas-default-gateway (required before modelsAsService)
- Configure DataScienceCluster — Enable KServe and modelsAsService in your DataScienceCluster
- Model Setup — Deploy sample models
- Validation — Verify the deployment
Database Setup
maas-api uses PostgreSQL as its persistence layer for API key metadata: hashed tokens, subscription bindings, expiration dates, and revocation state. The database must be reachable before maas-api starts; the pod will crash-loop until the connection succeeds and the schema migration completes.
Create the maas-db-config Secret in your ODH/RHOAI namespace (typically opendatahub for ODH or redhat-ods-applications for RHOAI):
kubectl create secret generic maas-db-config \
-n opendatahub \
--from-literal=DB_CONNECTION_URL='postgresql://username:password@hostname:5432/database?sslmode=require'
Connection string format:
Development
For development, you can deploy a PostgreSQL instance and Secret using the setup script:
Setting the namespace: The script defaults to opendatahub. Set the NAMESPACE environment variable if your MaaS deployment uses a different namespace:
# RHOAI uses redhat-ods-applications
NAMESPACE=redhat-ods-applications ./scripts/setup-database.sh
# Custom namespace
NAMESPACE=my-maas-namespace ./scripts/setup-database.sh
The full scripts/deploy.sh script also creates PostgreSQL automatically when deploying MaaS.
Using deploy.sh with an external database
If you use scripts/deploy.sh, you can supply your own PostgreSQL connection string with the --postgres-connection flag. This skips the built-in POC PostgreSQL deployment and creates the maas-db-config Secret automatically:
Restarting maas-api
If you add or update the Secret after the DataScienceCluster already has modelsAsService in managed state, restart the maas-api deployment to pick up the config:
This is not required when the Secret exists before enabling modelsAsService in your DataScienceCluster.
Create Gateway
Create maas-default-gateway in openshift-ingress before enabling modelsAsService in your DataScienceCluster.
scripts/deploy.sh runs this step automatically in route mode. Run the script yourself when installing via DataScienceCluster first, using clusterip mode, or on disconnected clusters.
| Environment | Command |
|---|---|
| ROSA, OSD, cloud (default) | ./scripts/setup-gateway.sh |
| On-prem, bare-metal, disconnected | INGRESS_MODE=clusterip ./scripts/setup-gateway.sh |
| Air-gapped (no GitHub fetch) | DISCONNECTED=true INGRESS_MODE=clusterip ./scripts/setup-gateway.sh |
Common overrides: CLUSTER_DOMAIN, CERT_NAME (route mode only), DRY_RUN, MAAS_MANIFEST_REF (pinned git ref for remote kustomize fallback). For the full variable list, TLS auto-detection order, and examples, see scripts/README.md.
Verify:
kubectl wait --for=condition=Programmed gateway/maas-default-gateway -n openshift-ingress --timeout=120s
Expected output:
For ClusterIP mode, also verify the Route:
Expected output:
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
maas-gateway-route maas.apps.example.cluster.com maas-default-gateway-openshift-default 443 reencrypt None
For ClusterIP architecture and troubleshooting, see Gateway patterns — ClusterIP + Route.
Required annotations
The script applies opendatahub.io/managed: "false" and security.opendatahub.io/authorino-tls-bootstrap: "true" on the Gateway. See TLS Configuration for custom certificates and Authorino integration. The authorino-tls-bootstrap annotation is interim until CONNLINK-528.
Authorino TLS
After the Gateway exists, run ./scripts/setup-authorino-tls.sh. For RHOAI/RHCL, set AUTHORINO_NAMESPACE=rh-connectivity-link.
Configure DataScienceCluster
After creating the database Secret and Gateways, create or update your DataScienceCluster. Choose your deployment method:
The operator deploys maas-controller, which self-bootstraps AITenant/models-as-a-service; that AITenant creates or adopts Tenant/default-tenant and reconciles the MaaS platform workloads (maas-api, gateway policies, telemetry). Create or update your DataScienceCluster with modelsAsService in Managed state:
kubectl apply -f - <<EOF
apiVersion: datasciencecluster.opendatahub.io/v2
kind: DataScienceCluster
metadata:
name: default-dsc
spec:
components:
kserve:
managementState: Managed
rawDeploymentServiceConfig: Headed
modelsAsService:
managementState: Managed
dashboard:
managementState: Managed
llamastackoperator:
managementState: Managed
EOF
LlamaStack Operator (GenAI Studio)
The llamastackoperator component is required for GenAI Studio functionality.
If you do not need GenAI Studio, you can omit it or set it to Removed.
You also need to enable the genAiStudio feature flag on OdhDashboardConfig —
see OdhDashboardConfig Feature Flags below.
Connectivity Link warning (ODH with Kuadrant)
When using ODH with Kuadrant (upstream), you may see Warning: Red Hat Connectivity Link is not installed, LLMInferenceService cannot be used in the Kserve status initially. This typically resolves after a few minutes as the operator reconciles. If it persists, run kubectl describe datasciencecluster default-dsc and check that the Kuadrant/Connectivity Link operator is installed and healthy.
Validate DataScienceCluster:
# Check DataScienceCluster status
kubectl get datasciencecluster default-dsc
# Wait for KServe and ModelsAsService to be ready (optional)
kubectl wait --for=jsonpath='{.status.conditions[?(@.type=="KserveReady")].status}'=True \
datasciencecluster/default-dsc --timeout=300s
kubectl wait --for=jsonpath='{.status.conditions[?(@.type=="ModelControllerReady")].status}'=True \
datasciencecluster/default-dsc --timeout=300s
# Verify maas-api deployment is running (use opendatahub for ODH, redhat-ods-applications for RHOAI)
kubectl get deployment maas-api -n opendatahub
kubectl rollout status deployment/maas-api -n opendatahub --timeout=120s
The maas-controller (deployed by the operator) will automatically create AITenant/models-as-a-service, which creates or adopts Tenant/default-tenant and reconciles:
- MaaS API (Deployment, Service, ServiceAccount, ClusterRole, ClusterRoleBinding, HTTPRoute)
- MaaS API AuthPolicy (maas-api-auth-policy) - Protects the MaaS API endpoint
- Gateway default AuthPolicy (gateway-default-auth) - Denies unauthenticated traffic
- Gateway default TokenRateLimitPolicy (gateway-default-deny) - Denies unsubscribed traffic
- TelemetryPolicy and Istio Telemetry (when telemetry is enabled)
- DestinationRule (when TLS is enabled)
- NetworkPolicy (maas-authorino-allow) - Allows Authorino to reach MaaS API
Tenant CR
With modelsAsService Managed, the Open Data Hub operator deploys maas-controller, which self-bootstraps AITenant/models-as-a-service in ai-tenants. The AITenant reconciler creates or adopts a namespace-scoped Tenant object. The resource name must be default-tenant (enforced via CEL validation). The Tenant CR lives in the models-as-a-service namespace (same namespace as MaaSSubscription and MaaSAuthPolicy). The authoritative API definition is in the maas-controller repo: tenant_types.go.
Nothing in spec is required for a default install. If you omit spec, the controller uses the same defaults as this guide: Gateway openshift-ingress / maas-default-gateway, and telemetry metric toggles use the defaults described below. During bootstrap, existing Tenant/default-tenant.spec.externalOIDC settings are automatically migrated to AITenant/models-as-a-service.spec.oidc. Going forward, set OIDC on AITenant/models-as-a-service.spec.oidc. For AITenant-managed tenants, Gateway and OIDC platform context comes from AITenant; existing Tenant.spec.gatewayRef and Tenant.spec.externalOIDC values are preserved for compatibility but ignored.
| Field | What to set |
|---|---|
spec.gatewayRef.namespace |
Legacy/unmanaged Tenant Gateway namespace. Ignored for AITenant-managed tenants. |
spec.gatewayRef.name |
Legacy/unmanaged Tenant Gateway name. For AITenant-managed tenants, use AITenant.spec.gateway.name. |
spec.apiKeys.maxExpirationDays |
Maximum allowed API key lifetime in days. When set, users cannot mint keys with a longer lifetime than this value (via expiresIn). Optional; if unset, the controller does not apply a cap through this field (see also maas-api / API_KEY_MAX_EXPIRATION_DAYS in your deployment). |
spec.externalOIDC.issuerUrl |
Legacy/unmanaged Tenant OIDC issuer URL. For AITenant-managed tenants, use AITenant.spec.oidc.issuerUrl. |
spec.externalOIDC.clientId |
Legacy/unmanaged Tenant OIDC client ID. For AITenant-managed tenants, use AITenant.spec.oidc.clientId. |
spec.telemetry.enabled |
Enable TelemetryPolicy and Istio Telemetry (default true). |
spec.telemetry.metrics.captureOrganization |
Include organization_id on metrics (default true). |
spec.telemetry.metrics.captureUser |
Include user labels on metrics (default false; privacy-sensitive). |
spec.telemetry.metrics.captureGroup |
Include group labels on metrics (default false; higher cardinality). |
spec.telemetry.metrics.captureModelUsage |
Include model labels on usage metrics (default true). |
Example (patch common values):
Development and early testing
Kustomize deployment can be used for development and early testing purposes. For production, use the Managed tab above.
Set modelsAsService to Removed so the operator does not deploy the MaaS API, then deploy MaaS directly from the canonical build root:
kubectl apply -f - <<EOF
apiVersion: datasciencecluster.opendatahub.io/v2
kind: DataScienceCluster
metadata:
name: default-dsc
spec:
components:
kserve:
managementState: Managed
rawDeploymentServiceConfig: Headed
modelsAsService:
managementState: Removed
dashboard:
managementState: Managed
EOF
Deploy the MaaS controller (run from the project root; ensure the maas-db-config Secret exists per Database Setup):
On a fresh cluster, use ./scripts/deploy.sh (or run install_maas_controller_crds_and_wait from scripts/deployment-helpers.sh before applying) so CRDs are Established before Config and other CRs in the same bundle.
Troubleshooting
If components do not become ready, run kubectl describe datasciencecluster default-dsc to inspect conditions and events.
OdhDashboardConfig Feature Flags
The RHOAI Dashboard uses feature flags in the OdhDashboardConfig resource to control which tabs
and features are visible in the UI. The operator creates this resource automatically when the
Dashboard component is deployed, but the following flags may need to be enabled manually.
Patch the OdhDashboardConfig in your applications namespace (typically redhat-ods-applications
for RHOAI or opendatahub for ODH):
kubectl patch odhdashboardconfig odh-dashboard-config \
-n redhat-ods-applications --type=merge \
-p '{"spec":{"dashboardConfig":{"genAiStudio":true,"observabilityDashboard":true}}}'
| Flag | Effect | Prerequisites |
|---|---|---|
genAiStudio: true |
Shows the GenAI Studio tab in the Dashboard | llamastackoperator set to Managed in DSC |
observabilityDashboard: true |
Shows the Observability tab in the Dashboard | COO, OpenTelemetry Operator installed; DSCI monitoring.metrics configured |
Namespace
For ODH installations, replace redhat-ods-applications with opendatahub (or your configured
applications namespace from DSCI spec.applicationsNamespace).
Flag names
These flags are defined in the odh-dashboard source. The OdhDashboardConfig CRD is of
API version opendatahub.io/v1alpha. The operator re-creates this resource with factory
defaults if it is deleted, but does not overwrite user changes to existing fields.
Creating Additional Tenants (Optional)
The default installation creates a single tenant in the models-as-a-service namespace. To create additional isolated tenants:
1. Create a Gateway
Each tenant requires its own Gateway. Follow the Gateway Patterns guide to create a gateway using the same pattern as maas-default-gateway.
Avoid hostname filters
Do not add hostname fields to Gateway listeners. Hostname-based routing can cause TLS/SNI issues. Use the default pattern with no hostname filter.
2. Create AITenant CR
Create an AITenant resource in the ai-tenants namespace:
apiVersion: maas.opendatahub.io/v1alpha1
kind: AITenant
metadata:
name: team-red
namespace: ai-tenants
spec:
gateway:
name: team-red-gateway # Must already exist in openshift-ingress
Apply the manifest:
The controller will automatically create:
- Tenant namespace: ai-tenant-team-red
- Tenant CR: default-tenant in the tenant namespace
- maas-api deployment: maas-api-team-red
- HTTPRoute and AuthPolicy for the tenant
3. Verify
# Check AITenant status
kubectl get aitenant team-red -n ai-tenants -o yaml
# Check tenant namespace was created
kubectl get namespace ai-tenant-team-red
# Check Tenant CR
kubectl get tenant default-tenant -n ai-tenant-team-red
For complete AITenant configuration options (OIDC, RBAC), see the AITenant CRD reference.
Next steps
- Deploy models. See Model Setup for sample model deployments.
- Perform validation. Follow the validation guide to verify that MaaS is working correctly.