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:
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:
- 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. - 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:
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:
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:
# 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:
uv add mlflow boto3 scikit-learn
Here is a complete test script (train_example.py):
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:
uv run train_example.py
4. Verification
- Open the MLflow UI at
http://localhost:5000. Select your run. You will see the artifact location clearly displayed ass3://mlflow-artifacts/.... - Open the MinIO Console at
http://localhost:9001. Navigate intomlflow-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.