MLflow 3.17 tracking server

How to run a remote MLflow tracking server

mlflow server listens on 127.0.0.1 port 5000 with a sqlite:///mlflow.db backend and ./mlartifacts for files. Reach it through an SSH tunnel, or bind --host 0.0.0.0 and list your hostname in --allowed-hosts, because unknown Host headers get a 403. Basic auth needs an admin password of at least 12 characters. Tested on MLflow 3.17.2.

5 min readML training and GPUs

At a glance

  • --host only controls which interface MLflow binds to; --allowed-hosts decides which Host headers get in.
  • Setting --allowed-hosts replaces the default of localhost plus private IPs.
  • Basic auth needs mlflow[auth], MLFLOW_FLASK_SERVER_SECRET_KEY and an admin password of 12 or more characters.
  • Clients need MLFLOW_TRACKING_URI, plus a username and password when auth is on.

Start the server

If you want one MLflow server on a remote box that training jobs on other machines can log to, start mlflow server. In MLflow 3.17.2, released on 2026-10-21 under Apache-2.0, mlflow ui prints the same help and runs the same tracking server (UI plus REST API), so older advice that mlflow ui is only a local viewer no longer applies.

Bash
pip install mlflow
mlflow server --backend-store-uri sqlite:///mlflow.db --artifacts-destination ./mlartifacts --port 5000

Out of the box, mlflow server binds to 127.0.0.1 on port 5000 with 4 workers. Runs and metrics go to sqlite:///mlflow.db (it reuses an existing ./mlruns store if one is present), and artifacts land in ./mlartifacts. Artifact serving is on, so clients upload and download files through the server.

Backend store and artifact store

A remote server has two storage locations. The backend store holds experiments, runs, parameters and metrics, and --backend-store-uri takes a SQLAlchemy URL such as postgresql://user:password@host:5432/mlflow; install a PostgreSQL driver like psycopg2-binary for that. The artifact destination holds files such as checkpoints and plots, and --artifacts-destination takes a local path or a bucket like s3://my-bucket, with boto3 installed on the server.

Because the server proxies artifacts, your training machines never need credentials for the bucket. These are the flags you'll actually touch.

FlagDefault in MLflow 3.17.2What to set on a remote server
--host127.0.0.1Keep it for an SSH tunnel or a local reverse proxy; 0.0.0.0 to listen on every interface
--port5000Any free port
--backend-store-urisqlite:///mlflow.dbA PostgreSQL URL for a shared server
--artifacts-destination./mlartifactsA local path or a bucket such as s3://my-bucket
--serve-artifactsOnLeave it on so clients upload through the server
--allowed-hostslocalhost plus private IPsEvery hostname your clients use, with the port if their URL has one
--workers4Leave it unless many clients log at once
--app-nameNot setbasic-auth to require a login

Reach the UI through an SSH tunnel

With --host at its default, nothing outside the box can reach port 5000. If you only need the UI from your own laptop, an SSH tunnel is the simplest safe way in.

Bash
ssh -N -L 5000:localhost:5000 user@mlflow-box

Then open http://localhost:5000 in your browser. It reaches the server's 127.0.0.1:5000 over SSH without a firewall change, and localhost is on the default allow list.

Open it to your network: --host 0.0.0.0 and --allowed-hosts

When training jobs on other machines must reach the server directly, bind it to every interface with --host 0.0.0.0. Don't mistake that for access control: the help text says --host "is NOT a security setting - it only controls network binding. To restrict which clients can connect, use --allowed-hosts."

Since MLflow 3.5.0, a security middleware checks the Host header of every request to block DNS rebinding. By default it lets in localhost and private IP ranges, so a lab network or VPC works without changes. A public hostname gets 403 and "Invalid Host header - possible DNS rebinding attack detected" until you add it.

Bash
mlflow server \
  --host 0.0.0.0 \
  --port 5000 \
  --backend-store-uri postgresql://mlflow:DB_PASSWORD@localhost:5432/mlflow \
  --artifacts-destination s3://my-mlflow-artifacts \
  --allowed-hosts "mlflow.example.com,mlflow.example.com:5000,localhost:5000,10.*"

Two details caught us in testing. Setting --allowed-hosts replaces the defaults, so localhost:5000 and 127.0.0.1:5000 were rejected until we listed them. And mlflow.example.com doesn't cover mlflow.example.com:5000, so list the name with the port your clients put in their URL, or use a wildcard such as 10.*, which matched addresses with any port.

Add a login with basic auth

If the server is reachable over a network, turn on MLflow's built-in basic auth. Install the extra first, because without Flask-WTF the server refuses to start with auth enabled.

Bash
pip install "mlflow[auth]"
openssl rand -hex 32 > ~/.mlflow_secret
export MLFLOW_FLASK_SERVER_SECRET_KEY="$(cat ~/.mlflow_secret)"
export MLFLOW_AUTH_ADMIN_PASSWORD="choose-a-long-password"
mlflow server --app-name basic-auth --host 0.0.0.0 --port 5000 --allowed-hosts "mlflow.example.com:5000"

Both variables are required. Without MLFLOW_FLASK_SERVER_SECRET_KEY the server stops with an error about CSRF protection, and MLflow ships no default admin password, so without MLFLOW_AUTH_ADMIN_PASSWORD (or admin_password in a config file named by MLFLOW_AUTH_CONFIG_PATH) it won't start. Passwords under 12 characters are rejected. The admin user is admin, and in our test the users were stored in basic_auth.db in the working directory.

Basic auth sends the password with every request, so put TLS in front with a reverse proxy before you expose the server beyond a private network.

Point your training scripts at the server

On the machines that run training, set MLFLOW_TRACKING_URI, or call mlflow.set_tracking_uri() in Python. With basic auth on, add MLFLOW_TRACKING_USERNAME and MLFLOW_TRACKING_PASSWORD; without them, every call fails with a 401.

Bash
export MLFLOW_TRACKING_URI=http://mlflow.example.com:5000
export MLFLOW_TRACKING_USERNAME=admin
export MLFLOW_TRACKING_PASSWORD="choose-a-long-password"
Python
import mlflow

with mlflow.start_run():
    mlflow.log_param("lr", 3e-4)
    mlflow.log_metric("loss", 0.42, step=1)

MLflow records runs, it does not watch them

Once your scripts log to the server, you have one place for metrics, parameters and artifacts. What you don't get is an alert when a training run crashes, hangs or finishes: MLflow's webhooks cover Model Registry and Prompt Registry events and AI Gateway budgets, not training runs.

Runpeek doesn't store metrics or history, and its links expire 30 minutes after the last update, so it complements the tracking server rather than replacing it. Keep the stall window at 30 minutes or less.

FAQ

What is the difference between mlflow ui and mlflow server?

In MLflow 3.17.2 they run the same tracking server, with the same help text and defaults: 127.0.0.1, port 5000 and a SQLite backend. Older MLflow versions described mlflow ui as a local viewer, so mlflow server is the clearer name to use in scripts and service files.

Why does my MLflow server return Invalid Host header?

The security middleware added in MLflow 3.5.0 rejects requests whose Host header isn't allowed, to block DNS rebinding. Add your hostname to --allowed-hosts, including the port if clients use one in the URL. Setting the flag replaces the default list of localhost and private IPs.

What is the default MLflow admin password?

There isn't one. MLflow 3.17 refuses to start basic auth until you set MLFLOW_AUTH_ADMIN_PASSWORD or an admin_password in the auth config file, and the password must be at least 12 characters. The admin username is admin.

How do I log to a remote MLflow server from a training script?

Set MLFLOW_TRACKING_URI to the server URL before the script starts, or call mlflow.set_tracking_uri() at the top. If the server uses basic auth, also set MLFLOW_TRACKING_USERNAME and MLFLOW_TRACKING_PASSWORD. Artifacts go through the server, so clients need no bucket credentials.

Can MLflow alert me when a training run fails?

Not on its own. MLflow's webhooks cover registry events and AI Gateway budgets, not training runs. To hear about a crash or a hang, add an alert that watches the running job, such as a stall alert that fires when progress stops.

Check on long runs from your phone

Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.

Get a live linkpip install tqdm-live

Sources