mlops••3 min read

Spinning Up Local S3 (MinIO) with Docker Compose for MLflow in 2 Minutes

Why you should stop logging MLflow artifacts to local directories and how to run a local S3-compatible MinIO object store using Docker Compose.

When learning MLOps or setting up a local machine-learning experiment pipeline, the easiest way to start with MLflow is running:

bash
mlflow ui

By default, this writes experiment metadata and artifacts (model binaries, confusion matrix plots, datasets) directly to a local ./mlruns directory.

While convenient for 5 minutes, logging to local paths breaks down quickly:

  1. Broken Paths in Multi-Container Setups: If your training runs inside a Docker container or Airflow worker, paths like /c/Users/... or /home/... become inaccessible to the MLflow tracking UI running elsewhere.
  2. Cloud Portability: Production MLOps systems invariably use S3, GCS, or Azure Blob storage. Relying on local paths prevents you from practicing how cloud artifact stores, bucket permissions, and URI schemes (s3://...) work.

The standard solution is MinIO, an open-source, S3-compatible object storage server you can run locally with Docker Compose.

Here is a 2-minute setup guide to configure MinIO with MLflow.


1. Minimal docker-compose.yml

Create a clean directory for your local MLOps lab and add the following docker-compose.yml:

yaml
version: '3.8'

services:
  minio:
    image: minio/minio:RELEASE.2024-05-10T01-41-38Z
    container_name: minio-s3
    restart: unless-stopped
    ports:
      - "9000:9000"   # S3 API endpoint
      - "9001:9001"   # Web Console UI
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadminpassword
    command: server /data --console-address ":9001"
    volumes:
      - minio_data:/data

  # Helper service to automatically create the MLflow bucket on startup
  createbuckets:
    image: minio/mc
    depends_on:
      - minio
    entrypoint: >
      /bin/sh -c "
      /usr/bin/mc alias set localminio http://minio:9000 minioadmin minioadminpassword;
      /usr/bin/mc mb --ignore-existing localminio/mlflow-artifacts;
      /usr/bin/mc anonymous set download localminio/mlflow-artifacts;
      exit 0;
      "

volumes:
  minio_data:

Start the containers:

bash
docker compose up -d
  • Access the MinIO Web Console at http://localhost:9001 (User: minioadmin, Password: minioadminpassword).
  • The S3 API is active at http://localhost:9000.

2. Launching MLflow with S3 Artifact Store

Now launch the MLflow Tracking Server, directing its default artifact store to your MinIO bucket:

bash
# Set environment variables for the AWS SDK (which boto3/mlflow uses)
export AWS_ACCESS_KEY_ID=minioadmin
export AWS_SECRET_ACCESS_KEY=minioadminpassword
export MLFLOW_S3_ENDPOINT_URL=http://localhost:9000

# Start MLflow UI pointing to SQLite for metadata and MinIO for artifacts
mlflow server \
  --backend-store-uri sqlite:///mlflow.db \
  --default-artifact-root s3://mlflow-artifacts/ \
  --host 0.0.0.0 \
  --port 5000

(On Windows PowerShell, use $env:AWS_ACCESS_KEY_ID="minioadmin", etc.)

Now MLflow UI is running at http://localhost:5000.


3. Logging an Experiment from Python

In your Python training script, all you need is boto3 installed alongside mlflow:

bash
uv add mlflow boto3 scikit-learn

Here is a complete test script (train_example.py):

python
import os
import mlflow
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier

# Configure S3 endpoint for the local script
os.environ["AWS_ACCESS_KEY_ID"] = "minioadmin"
os.environ["AWS_SECRET_ACCESS_KEY"] = "minioadminpassword"
os.environ["MLFLOW_S3_ENDPOINT_URL"] = "http://localhost:9000"

# Point to MLflow Tracking Server
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("iris-classification-local")

with mlflow.start_run():
    X, y = load_iris(return_X_y=True)
    n_estimators = 50
    model = RandomForestClassifier(n_estimators=n_estimators, random_state=42)
    model.fit(X, y)

    # Log parameters and metrics
    mlflow.log_param("n_estimators", n_estimators)
    mlflow.log_metric("accuracy", model.score(X, y))

    # Log model artifact to MinIO S3
    mlflow.sklearn.log_model(model, artifact_path="model")
    print("Experiment run completed and model logged to MinIO!")

Run the script:

bash
uv run train_example.py

4. Verification

  1. Open the MLflow UI at http://localhost:5000. Select your run. You will see the artifact location clearly displayed as s3://mlflow-artifacts/....
  2. Open the MinIO Console at http://localhost:9001. Navigate into mlflow-artifacts. You will see the model binary (model.pkl), conda environment specs, and MLmodel descriptors organized cleanly inside standard object keys.

Summary

Switching from raw filesystem logging to a local MinIO container requires only a few minutes and a single docker-compose.yml file. In return, you gain an architecture that matches production cloud environments (AWS S3, Google Cloud Storage), eliminating path issues and making your ML pipelines container-ready.

Technical Discussion (0)

Ask a question or reply to a thread below. Your email is never displayed publicly.

No comments yet. Start the technical discussion above.