docs/docs/self-hosting/workspaces/workspace-providers.mdx
Workspace providers (also referred to as workspace stores) are pluggable backends that manage workspace metadata and determine which workspaces are visible to users. This architecture allows MLflow to integrate with existing platform constructs (for example, Kubernetes namespaces or identity providers) while providing a default SQL-backed implementation.
Workspace providers implement mlflow.store.workspace.abstract_store.AbstractStore. The tracking server instantiates a single provider per process and uses it for both request routing and CRUD APIs.
from abc import ABC, abstractmethod
from typing import Iterable
from mlflow.entities import Workspace
class AbstractStore(ABC):
@abstractmethod
def list_workspaces(self) -> Iterable[Workspace]: ...
@abstractmethod
def get_workspace(self, workspace_name: str) -> Workspace: ...
def create_workspace(self, workspace: Workspace) -> Workspace:
raise NotImplementedError
def update_workspace(self, workspace: Workspace) -> Workspace:
raise NotImplementedError
def delete_workspace(self, workspace_name: str) -> None:
raise NotImplementedError
def get_default_workspace(self) -> Workspace:
raise NotImplementedError
def resolve_artifact_root(
self, default_artifact_root: str | None, workspace_name: str
) -> tuple[str | None, bool]:
return default_artifact_root, True
Concrete providers may implement the optional methods to support provisioning, updates, or custom artifact routing.
--workspace-store-uri, MLFLOW_WORKSPACE_STORE_URI, or leave it unset to reuse the backend store URI.postgresql://, mysql://, sqlite:///..., http(s)://).mlflow.workspace_provider entry point. Register a builder that returns an AbstractStore subclass and expose it through your package's entry_points configuration.The default provider is mlflow.store.workspace.sqlalchemy_store.SqlAlchemyStore. It persists workspace metadata in the MLflow tracking database and is automatically used when workspaces are enabled without specifying a custom provider.
workspaces table alongside the tracking backendget_default_workspace() returns the reserved default workspacedefault_artifact_root can be stored per workspace; when set, new experiments use <workspace_default_artifact_root>/<experiment_id> (no /workspaces/<workspace> prefix)resolve_artifact_root() enables the default /workspaces/<workspace>/<experiment_id> prefixing behavior and supports the per-workspace override abovemlflow server \
--backend-store-uri postgresql://localhost/mlflow \
--enable-workspaces
# Uses default SQL provider (SqlAlchemyStore)
To point at a different backend (for example a dedicated catalog database), provide --workspace-store-uri or MLFLOW_WORKSPACE_STORE_URI.
For deployments requiring dynamic workspace-specific artifact resolution beyond URI prefixing or custom artifact roots, artifact repositories can optionally implement the for_workspace() hook exposed by mlflow.store.artifact.artifact_repo.ArtifactRepository:
from mlflow.store.artifact.artifact_repo import ArtifactRepository
from mlflow.store.artifact.s3_artifact_repo import S3ArtifactRepository
class WorkspaceAwareS3ArtifactRepository(ArtifactRepository):
def for_workspace(self, workspace_name: str | None) -> "ArtifactRepository":
"""
Return a workspace-scoped repository instance.
"""
if workspace_name == "team-sensitive":
# Use dedicated bucket with specific credentials
return S3ArtifactRepository(
artifact_uri="s3://team-sensitive-bucket",
access_key_id=get_team_credentials(workspace_name),
)
# Default behavior
return self
Most deployments use the default URI prefixing approach (/workspaces/<workspace>/<experiment_id>), which works with all existing artifact repository implementations. For a simpler adjustment, set a workspace's default_artifact_root or override resolve_artifact_root() in a custom provider. For more dynamic needs, implement for_workspace() on an artifact repository to pick workspace-specific storage.
Workspace middleware seeds the request workspace ContextVar (mlflow.utils.workspace_context). Providers and authentication plugins can call mlflow.utils.workspace_context.get_request_workspace() to retrieve the active workspace name (or None when workspaces are disabled).
Providers can use these for custom authorization logic.