ML Projects
Use ml to sync machine learning metadata from an MLflow tracking server. Unlike every
other section, ml does not read files from the repository — the config points the CLI
at MLflow, and the CLI pulls the models, experiments, runs, datasets, and artifacts from
there on each sync.
MLflow is currently the only supported source type.
Config shape
ml:
- name: Recommendations
description: Ranking models for the product feed.
type: model
ownership:
team: ml-platform
email: ml@company.com
source:
type: mlflow
url: http://localhost:5000
models:
- name: feed-ranker
description: Ranks feed items for a user session.
problemType: ranking
- name: Recommendations Training
description: Training experiments behind the feed ranker.
type: training
ownership:
team: ml-platform
source:
type: mlflow
urlEnv: MLFLOW_URL
tokenEnv: MLFLOW_TOKEN
experiments:
- name: feed-ranker-sweeps
Project types
type decides which sub-list the project may declare, and the two are mutually
exclusive:
type | Must declare | Must not declare | What is synced |
|---|---|---|---|
model | models (at least one) | experiments | Registered models, their versions, production models, and evaluations |
training | experiments (at least one) | models | Experiments, runs, datasets, and artifacts |
Training projects are synced before model projects in a single run, regardless of the order they appear in the file. A model project's evaluations are only synced when the experiment they belong to was itself synced from the same MLflow server in that run — evaluations pointing at an unsynced experiment are skipped and reported in the output.
Source
| Field | Required | Description |
|---|---|---|
source.type | yes | Must be mlflow. |
source.url | one of | Tracking server URL, inline. |
source.urlEnv | one of | Name of an environment variable holding the URL. |
source.tokenEnv | no | Name of an environment variable holding the MLflow token. |
Exactly one of url and urlEnv must be set — declaring both, or neither, fails
validation. Prefer urlEnv in CI so the tracking server address is not committed.
Environment variables named by urlEnv and tokenEnv are checked during validation:
if the variable is unset or empty, the sync stops before contacting anything.
ml[1].source: specify either url or urlEnv, not both
ml[1].source.urlEnv: environment variable MLFLOW_URL is not set or empty
Project fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Project name. |
type | string | yes | model or training. |
description | string | no | Short summary. |
ownership.team | string | no | Owning team. |
ownership.email | string | no | Contact email. |
source | object | yes | See above. |
models | list | for model | See below. |
experiments | list | for training | Each entry needs a name matching an MLflow experiment. |
Model fields
name is required. Everything else is optional model-card metadata:
description, problemType, domain, license, intendedUse, limitations,
recommendations, considerations.
problemType, when set, must be one of classification, regression, ranking,
generation, embedding, other.
Repeat syncs are fast
After the first sync, each run only pulls what changed in MLflow since the last one. Syncing a project nobody has touched costs almost nothing, so this is safe to run on every push.
Use --ml-full to re-read everything from scratch. It is what you want when
backfilling history, or recovering after a sync failed part way through.
The token needs read access as well as write — mlstudio:read alongside
mlstudio:write — because the CLI has to look up when it last synced.
Sync output
🤖 Syncing 2 ML projects...
• Recommendations Training (training)
incremental since 2026-08-11T09:14:22Z
✓ ML project synced: Recommendations Training (4 experiments, 3 runs, 11 artifacts)
• Recommendations (model)
incremental since 2026-08-11T09:14:25Z
✓ ML project synced: Recommendations (1 models, 1 versions, 8 versions unchanged, 1 evaluations, 0 evaluations skipped)
Projects that have never been synced print full sync (no previous sync recorded)
instead.
--dry-run shows exactly what a real sync would send, without sending it.