runyard

A training tracker that fits in one binary.

runyard is a self-hosted tracker for ML and deep learning runs: status, metrics, samples, logs and checkpoints in a phone-first web UI, notifications when a run finishes, fails or goes silent, and an MCP server so agents can read everything. Far fewer features than W&B or MLflow, on purpose.

runyard overview, light theme runyard overview, dark theme

Five minutes

Run the server with Docker Compose. One Go binary and PostgreSQL; media and artifacts go to a directory or an S3/R2 bucket.

# server
git clone https://github.com/cobanov/runyard && cd runyard
docker compose up -d --build
docker compose exec app /runyard-server key create my-laptop

Log from Python in three lines. Calls are queued and sent from a background thread, so the library never stops training.

# training script
import runyard as ry

run = ry.init(project="my-project", config={"lr": 3e-4})
run.log({"loss": loss.item()}, step=step)
run.image("samples", grid, step=step)
run.alert("loss is NaN", level="error")

What it does

Logging

Three lines of Python, or no code change at all from tools that speak Weights & Biases, TensorBoard or MLflow. Integrations for transformers, TRL, Accelerate, Lightning, Keras, XGBoost, LightGBM, scikit-learn and torchtune.

Reading

A phone-first web UI with live charts, samples, logs and config diffs between runs. A CLI with --json, and an MCP server so coding agents can read runs and alerts.

Notifications

ntfy, Slack, Discord, Telegram, a webhook or web push to the installed app. Checks raise alerts from the data: a NaN loss, a crossed threshold, a stuck run.

Machine load

GPU utilisation, memory, temperature and power per host from the runs themselves, next to CPU and RAM. One glance says what is training and how the machine is doing.

Self-hosted

PostgreSQL, a media directory or an S3-compatible bucket, optional users and teams. Reach it over Tailscale, a tunnel, or bind it to a port.

Honest states

A clean exit marks a run finished, an exception failed, and five minutes without a heartbeat crashed. What could not be sent is saved at exit for runyard sync.