> ## Content Index
> Fetch the complete content index at: https://devopscube.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# MLflow Tutorial for Beginners (A Practical Guide)
- URL: https://devopscube.com/mlflow-tutorial/
- Published: 2026-09-16T07:03:26.000Z
- Updated: 2026-09-16T07:03:26.000Z
- Author: Bibin Wilson
- Tags: #syntax-highlight, #blog, MLOPS

In a previous [MLOps](https://devopscube.com/devops-to-mlops/) blog, we learned how [Kubeflow Trainer](https://devopscube.com/kubeflow-trainer/) helps run distributed training workloads on Kubernetes. But once model training is completed, we have a new problem.

How do we know:

- Where do we store the trained model?
- Which dataset created this model?
- How to compare models from different training runs?
- Which model version should go to production? and more..

This is where [MLflow](https://mlflow.org/docs/latest/self-hosting/?ref=devopscube.com) helps you.

In this blog, we will explore MLflow and its components in detail and provide a clear guide to using it effectively.

By the end of this guide, you will learn the following:

- What is Experiment Tracking?
- Understanding the MLflow Architecture
- How Kubeflow Pipelines Talk to MLflow
- Deploying MLflow on Kubernetes (Hands-on)
- Training an Employee Attrition Model Locally and Tracking Runs with MLflow (Hands-on)
- Registering and Managing Models in the MLflow Model Registry

Before we dive deep into MLflow, you need to understand what experiment tracking is. 

This way, you will be able to relate to MLflow better.

## What is Experiment Tracking?

In [machine learning](https://developers.google.com/machine-learning/intro-to-ml/what-is-ml?ref=devopscube.com), each time you train a model, a new training run is created. An **experiment is a collection of those training runs.**

For training, we pick an algorithm, set the hyperparameters, point it to a dataset version, and train. If something changes, we train again.

We have explained this in the [Training the Model](https://newsletter.devopscube.com/p/mlops-training-the-model?ref=devopscube.com) newsletter.

**Experiment tracking** means automatically logging important information about a training run. For example, during every training run, the following four things get captured.

- **Parameters:** Algorithm, hyperparameters, dataset version.
- **Metrics:** Accuracy, F1 score, training time.
- **Artifacts:** The model file, environment files, etc
- **Metadata:** Info on artifact location, model signature, input example, custom metadata, etc.

![Diagram illustrating how experiment tracking records a machine learning training run, including parameters, metrics, artifacts, and metadata.](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/b42275ec-5860-4665-8d44-ca58c88155fa/image.png?t=1784608393)

It is **similar to how we track commit SHA, build number, logs, etc in the CI system**. So a training run without tracking is like a CI build without build history. 

The most widely used tool for experiment tracking is MLflow.

## What is MLflow?

MLflow is the Git for machine learning experiments.

It is an open-source platform for tracking and managing the complete lifecycle of a model. 

Meaning, who trained it, with what parameters, on what data, what the results were, and which version is approved for production.

By tracking every training run, MLflow makes it **easy for data scientists to compare experiments** and identify which combination of data, parameters, and code produced the best-performing model. 

Since all the metadata is stored, **you can also reproduce a training run** using the same code, parameters, and dataset.

💡

****Important Note:** MLflow has a lot of LLM and agent-related features (tracing, prompt registry, evaluation). In this blog, I have only covered the classic ML part.

The following diagram illustrates the components of MLflow.

![MLflow tracking architecture – ML code and training pipelines log metadata and artifacts through the MLflow SDK to the Tracking Server, backend store, artifact store, and model registry](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/b741bcb4-754c-49eb-8148-894fd4cf76e9/image.png?t=1784608438)

MLflow operates as a client-server system, with four core components:

1. **MLflow SDK (Client):** A [Python](https://devopscube.com/python-for-devops/) package used to connect to the MLflow tracking server. You can install it on your local system or call it in ML training workflows to push the details to MLflow.
2. **Tracking server:** A lightweight FastAPI-based web server with a UI and a REST API. Your training code sends data to it over HTTP.
3. **Backend store:** A relational database that stores the metadata of experiments, runs, traces, etc. MLflow supports PostgreSQL, MySQL, SQLite, and MSSQL as backends.
4. **Artifact store:** This component stores artifacts such as model weights, images, and data files. For our attrition model project, this is where `model.skops` ends up after training. The artifact store is implemented using object storage services such as AWS S3, MinIO, GCS, Azure Blob Storage, etc.

## MLFow Functional components

MLflow has the following three key functional components.

1. **MLflow Experiments:** An experiment is a logical container for your ML work. For example, everything related to the attrition model is part of a single experiment.
2. **MLflow Run:** A run is a single execution of your training code inside an experiment. Every run records the parameters, metrics, artifacts, and code version used for that training. If you train the model 50 times with different hyperparameters, you get 50 runs in a single experiment.
3. **Model Registry:** A centralized repository for managing versioned machine learning models. Once you identify the best-performing model from your experiment runs, you can register it in the Model Registry.

You will understand all these better in the hands-on covered in the upcoming sections.

## How Kubeflow Pipelines Talks to MLflow?

Here is the common question everyone has.

We already have [Airflow](https://devopscube.com/apache-airflow-on-kubernetes/), [DVC](https://devopscube.com/dvc-tutorial-for-beginners/), [Feast](https://devopscube.com/setup-feature-store-feast-on-kubernetes/), and Kubeflow in our stack. Where exactly does MLflow fit in the MLOps stack?

The integration happens **inside the training script** that runs as part of a [Kubeflow Pipelines](https://devopscube.com/kubeflow-pipelines/).

When the training component executes, the script connects to the **MLflow Tracking Server** using the configured tracking URI.

![How Kubeflow Pipeline sends data to MLflow](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/33651cb4-7fd8-4225-8e49-79e62caa9844/image.png?t=1784205494)

During training, **it logs all model-related information to MLflow**, including hyperparameters, evaluation metrics, model artifacts, and metadata. 

Model artifacts are then pushed to the configured artifact store (such as Amazon S3).

## Setup Prerequisites

Below are the prerequisites for the hands-on section we will do.

- [Kubernetes Cluster](https://devopscube.com/setup-kubernetes-cluster-kubeadm/)
- [Kubectl](https://devopscube.com/kubectl-set-context/)
- [Helm](https://devopscube.com/create-helm-chart/)
- [AWS CLI](https://devopscube.com/install-configure-aws-cli-linux/) configured with permissions for IAM roles, S3, and [EKS](https://devopscube.com/create-aws-eks-cluster-eksctl/).

💡

****Important Note:** This setup guide is based on EKS cluster with S3 pod identity integration. If you are trying this out on different setup, you need to modify the S3 integration accordingly.

## Clone Repository

We have pushed all the code files we will use in the hands-on part to our [GitHub repository](https://github.com/techiescamp/mlops-for-devops/?ref=devopscube.com).

Fork or sync your forked MLOps repo to get the latest code changes. Check out [this guide](https://newsletter.devopscube.com/p/how-to-keep-your-fork-in-sync-with-upstream?ref=devopscube.com) to learn how to keep your fork up to date.

You can find the files we will use in the following directories.

```bash
.
├── phase-2-enterprise-setup
│   └── mlflow
│        ├── register_model.py
│        ├── requirements.txt
│        └── train_and_log_model.py
└── platform-tools
    └── mlflow
        ├── helm/
        ├── eks-s3.sh
        └── test-pod.yaml
```

Here, you can see the Helm chart to set up MLflow and scripts to train, log, and register a model in MLflow.

## MLflow on Kubernetes (EKS Hands-on)

Now let’s put everything we learned into practice with a hands-on setup. Here is what we will do.

- We will set up MLflow on Kubernetes.
- Configure AWS S3 as an artifact store.
- Run our employee attrition training locally to train and log employee attrition model details.
- Explore the experiment, run details, artifacts, and registered model in the MLflow UI.

### How to Set Up MLflow on Kubernetes?

Follow the steps to set up MLflow on an EKS cluster.

#### Step 1: Create an S3 bucket

We will use an AWS S3 bucket as the backend Artifact store. First, you need to create a bucket.

Replace dcube-mlflow-artifact-store in the following command with a unique bucket name.

```bash
aws s3api create-bucket \
  --bucket dcube-mlflow-artifact-store \
  --region us-west-2 \
  --create-bucket-configuration LocationConstraint=us-west-2 \
  --no-cli-pager
```

Verify the bucket exists.

```bash
aws s3api head-bucket \
  --bucket dcube-mlflow-artifact-store \
  --no-cli-pager
```

#### Step 2: Deploy PostgreSQL Using Helm Chart

Since we will be using PostgreSQL as the MLflow backend store, let's deploy it using [Helm](https://devopscube.com/helm-best-practices-essential-tips-to-know/).

```bash
helm install mlflow-postgres oci://registry-1.docker.io/bitnamicharts/postgresql \
  --namespace mlflow \
  --create-namespace \
  --set auth.username=mlflow \
  --set auth.password=mlflow123 \
  --set auth.database=mlflow \
  --set primary.persistence.size=10Gi
```

⚠️

Here, I have given the username and password directly. In production, store the secrets in external secret managers like AWS SecretsManager and call them during deployment.

Ensure the Postgres pod is running.

```bash
$ kubectl get po -n mlflow

NAME                           READY   STATUS    RESTARTS   AGE
mlflow-postgres-postgresql-0   1/1     Running   0          35m
```

#### Step 3: Update the S3 bucket in the Helm Values File

The next step is to customize the Helm values for the MLflow installation.

Move into the **platform-tools/mlflow/helm** folder.

```bash
cd platform-tools/mlflow/helm
```

You will find the **mlflow.yaml** values file inside it.

In that file, replace <bucket-name> with your S3 bucket name in artifactsDestination.

![mlflow helm values file for s3 bucket](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/1e60001f-8c4d-4426-9230-eb927d653441/image.png?t=1784089710)

The **mlflow.yaml** also contains the predefined PostgreSQL service endpoint we set up in the previous step. We are enabling NodePort in the values to access the MLflow UI.

Also, in the values file, you will see options like:

```yaml
server:
  flag_options:
    - serve_artifacts
  value_options:
    host: "0.0.0.0"
    allowed_hosts: "*"
    cors_allowed_origins: "*"
```

In this, **serve\_artifacts** makes MLflow upload the artifact to the artifact store. Without it, the client uploads the artifact directly to S3, and you need to provide the client with S3 credentials.

The **host** makes the MLflow pod listen on all interfaces.

The **allowed\_hosts** and **cors\_allowed\_origins** are security options that control which hosts and origins are allowed to connect to MLflow. 

If the proper host address is not defined in **allowed\_hosts**, you will get the following error when you try to connect to MLflow.

```bash
Invalid Host header - possible DNS rebinding attack detected

```

Here, we set both to \*, so that MLflow can be accessed without restrictions.

💡

Use wildcard(\*) only for testing, use proper host URL for production environment.

We will see about the security options in the upcoming section.

#### Step 4: Deploy MLflow

Now, let's deploy MLflow on the Kubernetes cluster using the Helm Chart.

Run the following helm install command inside the **platform-tools/mlflow/helm** folder.

```bash
helm install mlflow . --namespace mlflow -f mlflow.yaml
```

Run the following command to check if the pods are up and running.

```bash
$ kubectl get po -n mlflow

NAME                             READY   STATUS    RESTARTS   AGE
mlflow-mlflow-68cddb7f64-r277z   1/1     Running   0          98s
mlflow-postgres-postgresql-0     1/1     Running   0          58m
```

#### Step 5: Access the MLflow UI

You can access the MLflow UI using the NodePort we exposed. You can get the NodePort using the following command.

```bash
kubectl get svc mlflow-mlflow -n mlflow
```

Or you can use the following port-forward command. Keep this 

```bash
kubectl port-forward deployment/mlflow-mlflow 5000:5000 -n mlflow
```

You should be able to access the UI at **localhost:5000** in your browser, as shown below.

![image showing mlflow ui being accessed over localhost](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/b063ef51-a374-40e6-9c08-18dc0250a49a/image.png?t=1784093733)

#### Step 6: Configure EKS Pod Identity for S3 Access

Next, you need to set up AWS EKS Pod Identity so that your MLflow deployment can **securely access an S3 bucket** without storing AWS credentials in Kubernetes.

All the steps required for the s3 role creation and eks pod identity association are part of the **eks-s3.sh** shell script in the **platform-tools/mlflow** folder.

Open the shell script and update the following variables shown in the image with your EKS cluster and bucket names.

![shell script to give mlflow access to aws s3](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/9a9721f0-9fc3-445d-a992-5bb4bf8b3292/image.png?t=1784096352)

Once updated, execute the shell script with the create input.

```bash
$ chmod +x eks-s3.sh 

$ ./eks-s3.sh create
```

You should see all the roles and association details in the script output. Now the setup is complete; let’s move on to the MLflow components setup

### Run the Training Script

⚠️

****Important:** The training script used in this blog is only meant to help you understand how MLflow works.

Now, we are going to run a training script, it does the following.

- Trains a scikit-learn Gradient Boosting model using the employee attrition dataset.
- Logs the model, training parameters, and evaluation metrics (Accuracy, Precision, Recall, F1-score, and ROC-AUC) to the MLflow Tracking Server.

You will find a **train\_and\_log\_model.py** script inside the **phase-2-enterprise-setup/mlflow** folder.

Now, open the Python script and **replace the MLFLOW\_TRACKING\_URI** with the EKS node IP and NodePort address. 

Alternatively, if you're using kubectl port-forward, set it to **http://127.0.0.1:5000** instead.

![Python script where MLFLOW_TRACKING_URI is updated with MLFlow URL](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/390b134b-060f-4df5-9f15-ae9be178c477/image.png?t=1784188239)

Now, create a Python environment and install the dependencies.

```bash
python3 -m venv venv                                    
source venv/bin/activate
pip install -r requirements.txt
```

Once installed, run the **train\_and\_log\_model** Python script.

```bash
python train_and_log_model.py
```

Once you execute the script, the `log_params()`, `log_metrics()`, and `set_tags()` send **metadata**, while `mlflow.sklearn.log_model()` sends the **actual trained model** (model.skops) to MLflow along with the files needed to reproduce and serve it.

### Explore the Run in the UI

Now, if you visit the MLflow UI, you will find the **employee-attrition experiment** created as shown below. 

Each execution of the training script **creates a new run** under this experiment.

![Image showing mlflow experiment details on MLFlow UI](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/113782df-f854-4907-a133-d713c3ed4634/image.png?t=1784188335)

If you click the **employee-attrition experiment,** it will show all the training runs. If you select a specific run, you can view everything logged during training, including Parameters, Metrics, Model artifacts, etc. 

The following animated GIF shows it better.

![GIF demo showing how to view the logged model in a mlflow run](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/811f5644-1c0e-4fb9-b0c9-f1d6aa9b1595/mlflow-log3.gif?t=1784199665)

💡

Every training run creates a model artifact, and you can view them under models.  
  
However, not all models become part of the Model Registry. Data scientists select the best model and register it so that it becomes part of the Model Registry. This process is covered in the section below.

If you expand the **Artifacts** section of a specific run and select the **MLmodel metadata file**, you can view the s3 artifact location. 

![viewing mlflow mlmodel metedata file in MLFlow UI](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/0eb1b455-e6cc-4c5c-b6a9-c933664f0ec7/image.png?t=1784114270)

If you browse the configured S3 bucket, you will find the same model files that MLflow uploaded during training.

### Model Signature

If you scroll down a little further in the MLmodel metadata file, you'll see the **model signature**, as shown below.

![mlflow model signature](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/41e1f44b-b62a-4ec2-9822-3315362bbf15/image.png?t=1784189003)

Every model **expects inputs in a certain schema** and format. If you pass inputs that don't match what the model expects, it may give the wrong predictions 

*So how do you know what inputs the model expects?* That's what a model signature solves

A model signature is the schema of a machine learning model. It **defines the expected input features**, their data types, the prediction output format, and optional inference parameters

For any consumer of the model, **it acts as a contract**. Meaning anyone pulling the model from the registry knows exactly what the model expects as input and what the expected output format is.

For teams deploying the model, the signature is like an API spec for the model. 

As a [DevOps engineer](https://devopscube.com/become-devops-engineer/), after deployment, you verify that the APIs work as expected using the API spec. Using the model signature, you can do the same for model deployments. 

From the tooling perspective, [CI/CD tools](https://devopscube.com/learning-ci-cd-tools/) can use the signature to compare the schema changes against the production model before deployment.

![image showing how mlflow model signature is used by different teams](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/51f8c503-bd83-45fa-beff-455ddbb875f3/image.png?t=1784777856)

💡

 The signature solves the feature order problem. It gives you the exact order of features that need to be passed to the model during inference.

### Comparing Training Runs

Training is usually not a one-time activity. 

Data scientists **train the model multiple times** using different algorithms, hyperparameters, and datasets. 

Then they can **select the runs in the UI** and **use the compare feature** to determine which training run produced the best model, as shown below.

![GIF demo shosing how to compare training runs on mlflow](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/3d3d3664-0b48-482e-a0e6-72a3c42931ed/mlflow-compare.gif?t=1784197559)

This makes it easy for data scientists to determine which run to deploy.

### Register & Promote the Best Model in Model Registry

After comparing multiple runs, the data scientist team selects the **best-performing model** and registers it in the **Model Registry**.

This creates a new version of the registered model that stores its artifacts, metadata, metrics, lineage, and version history.

We will use the Python script **register\_model.py** inside the **phase-2-enterprise-setup/mlflow** folder to register and promote the model.

Open the script and update the tracking uri and run id.

![updating mlflow tracking uri and run id in the model register and promoting script](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2026/09/image-65.png)

To get the Run ID, open the model run you want to register and promote, you will get the Run ID as shown below.

![finding run id in mlflow ui](https://storage.ghost.io/c/5f/2f/5f2f4d20-2abf-4534-8d40-7aa233aedd43/content/images/2026/09/image-64.png)

Once the details are updated, run the script **register\_model**.

```bash
python register_model.py
```

This script registers the model and assigns the **production alias champion** tag to the model.

The **@champion** alias acts like a **:stable** container image tag. It always points to the model version approved for production deployment. 

This allows deployment systems to reference **models:/my-model@champion** without hardcoding version numbers.

![MLFLow UI showing registered ml models with @champion alias](https://media.beehiiv.com/cdn-cgi/image/fit=scale-down,quality=80,format=auto,onerror=redirect/uploads/asset/file/ad26eae3-d836-4d07-8401-2f59f04e49c2/mlflow-registry.gif?t=1784197648)

💡

In production MLOps setups, this entire workflow is automated through CI/CD pipelines.  
  
After a model passes predefined quality gates such as ****accuracy ≥ 95%**, ****F1 score ≥ 94%**, and ****data quality validation**, the pipeline automatically registers the model, promotes it with an alias like ****Champion**, and deploys it to the inference platform.

## Cleanup

Once you have tested the setup, run the following commands to clean it up.

```bash
$ helm uninstall mlflow -n mlflow

$ helm uninstall mlflow-postgres -n mlflow

$ ./eks-s3.sh cleanup

$ aws s3 rb s3://dcube-mlflow-artifact-store --force
```

## What's the Difference Between Model Registry and Artifact Store?

Many people who use MLflow confuse the Model Registry and the Artifact Store.

To put it simply, the Model Registry is a feature of the MLflow tracking server that versions models. 

And the artifact store is an external object storage configured in MLflow to store the model files.

## MLflow Security Features

After MLflow version 3.5.0, new security features have been added to MLflow.

The first two features we saw in Step 3.

- **DNS Rebinding Protection** (allowed\_hosts) - This only accepts traffic from hosts in the allowed list.
- **CORS Protection (cors\_allowed\_origins)** \- Only allow specific domains or browsers to call the MLflow tracking API. Even if you open the MLflow UI, if the origin does not match, you will get a 403 status code.
- **Clickjacking Prevention** \- Stop others from embedding the MLflow UI into their own page.
- **Security Headers** \- Stop the browser from accepting unsupported or not-recommended file types or files with hidden scripts to prevent attacks from the browser.

## Conclusion

In this blog, we have learned about MLflow and what it's for.

And we have registered our best model as **employee-attrition@champion**.

Now, how do we actually serve it to real users at scale?

In the next blog, we will look at what [KServe](https://devopscube.com/deploy-ml-model-kubernetes-kserve/) is, and how it pulls the registered **@champion** model from MLflow and deploys it on Kubernetes as a scalable inference service.