1.15 Model Tags, Aliases, and Champion/Challenger Promotion
Key Takeaways
- `client.set_registered_model_alias(name, alias, version)` points a named alias at a version; `delete_registered_model_alias(name, alias)` removes it.
- An alias is a mutable pointer and is exclusive: assigning `@champion` to a new version automatically moves it off the previous one.
- Tags are descriptive key/value metadata set with `set_model_version_tag` and removed with `delete_model_version_tag`; unlike aliases, many versions can carry the same tag.
- Aliases replace the legacy stages `None`, `Staging`, `Production`, and `Archived`, which do not exist for models in Unity Catalog.
- Loading by alias — `models:/catalog.schema.model@champion` — lets promotion happen with zero changes to downstream client code.
1.15 Model Tags, Aliases, and Champion/Challenger Promotion
Legacy MLflow gave every model version one of four fixed stages. Unity Catalog removes them and provides two orthogonal mechanisms instead: aliases, which are exclusive named pointers used for routing, and tags, which are non-exclusive labels used for description and search.
Managing Lifecycle: Model Aliases and Tags vs. Legacy Stages
In legacy MLflow, models used hardcoded stages (Staging, Production). Unity Catalog deprecates stages in favor of Model Aliases and Tags, providing vastly greater deployment flexibility:
+---------------------------------------------------------------------------------------------------+
| MODEL ALIASING AND TAGGING PATTERN |
+---------------------------------------------------------------------------------------------------+
| Model Version 2 <--- Assigned Alias `@champion` (Points production scoring queries here) |
| Model Version 3 <--- Assigned Alias `@challenger` (Points canary/A-B evaluation queries here) |
| Model Version 3 <--- Assigned Tag `validation_status: approved_by_risk_team` |
+---------------------------------------------------------------------------------------------------+
Aliases act as mutable named pointers: setting one on a new version automatically removes it from the previous version, so promotion never needs a separate "demote" call. Tags are independent key/value metadata that stay attached to the version that carries them. The consolidated example below exercises both APIs together.
Aliases vs. Tags: the Distinction That Decides Answers
| Alias | Tag | |
|---|---|---|
| Shape | A name pointing at exactly one version | A key/value pair on a version (or on the registered model) |
| Exclusivity | One version per alias; reassigning moves it | Any number of versions may share a tag |
| Purpose | Routing — "which version should production load?" | Description and search — "which versions passed risk review?" |
| Set with | client.set_registered_model_alias(name, alias, version) | client.set_model_version_tag(name, version, key, value) |
| Remove with | client.delete_registered_model_alias(name, alias) | client.delete_model_version_tag(name, version, key) |
| Referenced in a URI | Yes — models:/cat.sch.model@champion | No |
from mlflow.tracking import MlflowClient
client = MlflowClient()
name = "prod_catalog.fraud_detection.payment_risk_classifier"
# Tags: descriptive, non-exclusive
client.set_model_version_tag(name, version=3, key="risk_review", value="approved")
client.delete_model_version_tag(name, version=1, key="risk_review")
# Aliases: exclusive routing pointers
client.set_registered_model_alias(name, alias="challenger", version=3)
client.set_registered_model_alias(name, alias="champion", version=3) # moves off v2
client.delete_registered_model_alias(name, alias="challenger")
The Champion/Challenger Promotion Workflow
- A retraining job registers version 3 and sets
@challengeron it. - An automated validation job loads
models:/…@challengerandmodels:/…@champion, scores both on a frozen holdout set, and compares. - If the challenger wins, the job tags the version (
validation="passed",promoted_on="2026-08-19") and callsset_registered_model_alias(..., "champion", 3). The alias moves off version 2 automatically; no downstream code changes. - The
@challengeralias is deleted so the next cycle starts clean. Version 2 remains in the registry with its tags intact, so rollback is a single alias reassignment.
Because serving endpoints and batch jobs reference the alias rather than a version number, promotion and rollback are metadata operations — nothing is redeployed.
Resolving an Alias and Rolling Back
Automation regularly needs to know which version an alias currently points at — to log it, to score against it, or to roll back to it:
mv = client.get_model_version_by_alias(
name="prod_catalog.fraud_detection.payment_risk_classifier",
alias="champion",
)
print(mv.version, mv.run_id, mv.tags)
Rollback is then a single reassignment pointing @champion back at the previous
version number. Nothing is rebuilt, no endpoint is recreated, and every job or endpoint
referencing models:/…@champion picks the change up on its next model resolution.
Recording the outgoing version as a tag — previous_champion="4" — before promoting
turns that rollback into a one-liner instead of an archaeology exercise.
Two Levels of Tag
Tags exist on both the registered model and each individual version, and the distinction is testable:
| Scope | API | Use for |
|---|---|---|
| Registered model — all versions | client.set_registered_model_tag(name, key, value) | Ownership, business domain, on-call team |
| A single version | client.set_model_version_tag(name, version, key, value) | Validation outcome, approval reference, promotion date |
Naming and Scope Rules
- A Unity Catalog model is always addressed by a three-level name,
catalog.schema.model. A two-level name is a workspace-registry model, where aliases are unavailable and the legacy stages still apply. - Aliases are scoped to one registered model, so
@championonprod.fraud.risk_modelis unrelated to@championonprod.churn.propensity. - Because an alias is exclusive there is nothing to disambiguate at load time: it resolves to exactly one version, or the load fails.
Loading Models for Batch and Real-Time Inference
Applications can load models from Unity Catalog deterministically by specifying either an Alias or an exact Version Number:
import mlflow.pyfunc
# 1. Load production model dynamically using Alias (Zero code change when champion updates)
champion_uri = "models:/prod_catalog.fraud_detection.payment_risk_classifier@champion"
production_model = mlflow.pyfunc.load_model(champion_uri)
predictions = production_model.predict(inference_pandas_df)
# 2. Load exact pinned model version for reproducible auditing
version_uri = "models:/prod_catalog.fraud_detection.payment_risk_classifier/2"
audit_model = mlflow.pyfunc.load_model(version_uri)
# 3. Load model as PySpark UDF for high-throughput distributed batch scoring
import mlflow.pyfunc
predict_udf = mlflow.pyfunc.spark_udf(spark, champion_uri, result_type="double")
scored_df = spark_features_df.withColumn("fraud_probability", predict_udf(*feature_cols))
In Unity Catalog Model Governance, what mechanism replaces legacy MLflow Stage Transitions (such as transitioning a model to 'Production' or 'Staging')?
An MLOps engineer wants to point the '@champion' alias of a registered model 'prod_catalog.ml.churn_model' to Version 4. What code achieves this?
A governance team wants to mark every model version that has cleared its compliance review, while a serving endpoint must always load whichever single version is currently approved for production. Which mechanisms fit these two needs?