Skip to content

Commit 60bbc86

Browse files
abidlabsclaudegradio-pr-bot
authored
Add server_url parameter for self-hosting Trackio servers (#510)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
1 parent 21c099a commit 60bbc86

14 files changed

Lines changed: 498 additions & 65 deletions

File tree

.changeset/vast-yaks-wait.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"trackio": minor
3+
---
4+
5+
feat: Add server_url and TRACKIO_SERVER_URL for self-hosted servers; space_id and TRACKIO_SPACE_ID take precedence when both are set

README.md

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ Trackio's main features:
3535
```
3636
and keep your existing logging code.
3737

38-
- **Local-first** design: dashboard runs locally by default. You can also host it on Spaces by specifying a `space_id` in `trackio.init()`.
39-
- Persists logs in a Sqlite database locally (or, if you provide a `space_id`, in a private Hugging Face Dataset)
40-
- Visualize experiments with a **Svelte 5** dashboard locally (or, if you provide a `space_id`, on Hugging Face Spaces)
38+
- **Local-first** design: dashboard runs locally by default. You can also send metrics to a Hugging Face Space with `space_id` for free or to a self-hosted Trackio server you run yourself with `server_url`
39+
- Persists logs in a Sqlite database locally (or on the remote target you chose: Space, or the machine hosting your self-hosted server)
40+
- Visualize experiments with a **Svelte 5** dashboard locally, on Hugging Face Spaces, or on your own host when you self-host the server
4141
- **LLM-friendly**: Built with autonomous ML experiments in mind, Trackio includes a CLI for programmatic access and a Python API for run management, making it easy for LLMs to log metrics and query experiment data.
4242
- Use `trackio query project --project <name> --sql "SELECT ..."` for read-only SQL when `trackio list` and `trackio get` are not enough
4343
- See the storage schema and direct query reference at https://huggingface.co/docs/trackio/storage_schema
@@ -155,6 +155,18 @@ trackio.init(project="my-project", space_id="username/space_id")
155155

156156
it will use an existing or automatically deploy a new Hugging Face Space as needed. You should be logged in with the `huggingface-cli` locally and your token should have write permissions to create the Space.
157157

158+
## Self-hosted Trackio server
159+
160+
You can run the Trackio dashboard and API on your own machine or infrastructure and point training jobs at it over HTTP. Pass the write-access URL from `trackio.show()` (which may include `write_token` in the query), or a base URL plus the `TRACKIO_WRITE_TOKEN` environment variable. The client sends that token on requests; it is not your Hugging Face token.
161+
162+
```py
163+
trackio.init(project="my-project", server_url="http://127.0.0.1:7860?write_token=YOUR_TOKEN")
164+
```
165+
166+
You can also set `TRACKIO_SERVER_URL` (and optionally `TRACKIO_WRITE_TOKEN` if the URL has no query string). If `space_id` / `TRACKIO_SPACE_ID` and `server_url` / `TRACKIO_SERVER_URL` are both set, Trackio uses the Hugging Face Space and ignores the self-hosted URL.
167+
168+
See the documentation: [Self-host the Server](https://huggingface.co/docs/trackio/self_hosted_server).
169+
158170
## Syncing Offline Projects to Spaces
159171

160172
If you've been tracking experiments locally and want to move them to Hugging Face Spaces for sharing or collaboration, use the `sync` function:

docs/source/_toctree.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
title: Track
1414
- local: launch
1515
title: Launch the Dashboard
16+
- local: self_hosted_server
17+
title: Self-host the Server
1618
- local: deploy_embed
1719
title: Deploy and Embed Dashboards
1820
- local: manage

docs/source/environment_variables.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@ export TRACKIO_DIR="/path/to/trackio/data"
1414

1515
Note: This environment variable applies as long as Trackio is not running in a Space with persistent storage enabled. If Trackio is running in a Space with persistent storage enabled (which is detected with the `PERSISTANT_STORAGE_ENABLED` env variable), then the Trackio data will be stored in `/data/trackio`.
1616

17+
### `TRACKIO_SERVER_URL`
18+
19+
Base URL of a self-hosted Trackio server (`http://` or `https://`). You may include `write_token` in the query string (as in the `full_url` from `trackio.show()`), or keep the URL bare and set `TRACKIO_WRITE_TOKEN` instead. When set, `trackio.init()` sends metrics to that server. Equivalent to passing `server_url=` to `trackio.init()`.
20+
21+
**Precedence:** If `TRACKIO_SPACE_ID` is also set (or `space_id` is passed in code), the Hugging Face Space is used and `TRACKIO_SERVER_URL` is ignored. Same rule when both `space_id` and `server_url` are passed: `space_id` wins.
22+
23+
See [Self-host the Server](self_hosted_server.md).
24+
25+
```bash
26+
export TRACKIO_SERVER_URL="http://127.0.0.1:7860"
27+
export TRACKIO_WRITE_TOKEN="YOUR_TOKEN"
28+
```
29+
30+
### `TRACKIO_WRITE_TOKEN`
31+
32+
The dashboard **write token** for a self-hosted Trackio server (same value as the `write_token` query parameter in the write-access URL). Use this when `TRACKIO_SERVER_URL` or `server_url` is a base URL without query parameters. The client sends this token on each request (for example as the `X-Trackio-Write-Token` header) so metric ingestion and uploads are authenticated when not running on Hugging Face Spaces.
33+
1734
### `TRACKIO_LOGO_LIGHT_URL` and `TRACKIO_LOGO_DARK_URL`
1835

1936
Customize the logos displayed in the Trackio dashboard for light and dark themes. You can provide URLs to custom logos. Note that both environment variables should be supplied; otherwise, the Trackio default will be used for any variable that is not provided.

docs/source/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
import trackio as wandb
1515
```
1616

17-
- **Local-first** design: dashboard runs locally by default. You can also host it on Spaces by specifying a `space_id`.
18-
- Persists logs locally (or in a private Hugging Face Dataset)
19-
- Visualize experiments with a Gradio dashboard locally (or on Hugging Face Spaces)
17+
- **Local-first** design: dashboard runs locally by default. You can also log to a Hugging Face Space (`space_id`) for free or to a **self-hosted** Trackio server (`server_url`)
18+
- Persists logs locally, in a Hugging Face Dataset when using Spaces, or on the machine hosting your self-hosted server
19+
- Visualize experiments with a Gradio dashboard locally, on Hugging Face Spaces, or on your own host when self-hosting
2020
- **LLM-friendly**: Designed for autonomous ML experiments with CLI commands and Python APIs that enable LLMs to easily log and query experiment data.
2121
- Everything here, including hosting on Hugging Face, is **free**!
2222

docs/source/self_hosted_server.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Self-host the Trackio server
2+
3+
You can run the Trackio dashboard and API on your own machine (or any host you control) and send metrics from training scripts to that server over HTTP. This is an alternative to logging to a Hugging Face Space via `space_id`.
4+
5+
## Run the server locally
6+
7+
Install Trackio, then start the dashboard. By default it listens on `127.0.0.1` and uses the port from `GRADIO_SERVER_PORT` if set, otherwise **7860** (see [Launch the Dashboard](launch.md)).
8+
9+
<hfoptions id="language">
10+
<hfoption id="Shell">
11+
12+
```sh
13+
trackio show
14+
```
15+
16+
</hfoption>
17+
<hfoption id="Python">
18+
19+
```py
20+
import trackio
21+
22+
trackio.show()
23+
```
24+
25+
</hfoption>
26+
</hfoptions>
27+
28+
Leave that process running while you train. The terminal prints a base URL for the UI and a URL that includes a **write token**; anyone who can reach that URL with the token can perform write operations supported by the server (for example renaming runs), so treat it like a secret on untrusted networks.
29+
30+
## Listen on all interfaces
31+
32+
To access the dashboard from another machine on your network (or from containers), bind to all interfaces:
33+
34+
<hfoptions id="language">
35+
<hfoption id="Shell">
36+
37+
```sh
38+
trackio show --host 0.0.0.0
39+
```
40+
41+
</hfoption>
42+
<hfoption id="Python">
43+
44+
```py
45+
import trackio
46+
47+
trackio.show(host="0.0.0.0")
48+
```
49+
50+
</hfoption>
51+
</hfoptions>
52+
53+
Ensure your firewall and security policies allow the traffic you intend.
54+
55+
## Point training code at your server
56+
57+
In your training script, pass a `server_url` and supply the **write token**: either embed `write_token` in the query string (the same URL the dashboard prints for write access, or the `full_url` return value from `trackio.show()`), or pass a base URL like `http://127.0.0.1:7860/` and set the `TRACKIO_WRITE_TOKEN` environment variable to that token. Metric ingestion and uploads require this token when the server is not on Hugging Face Spaces. You can also set `TRACKIO_SERVER_URL` instead of passing `server_url` in code.
58+
59+
```py
60+
import trackio
61+
62+
trackio.init(
63+
project="my-project",
64+
server_url="http://127.0.0.1:7860?write_token=YOUR_TOKEN",
65+
)
66+
trackio.log({"loss": 0.25})
67+
trackio.finish()
68+
```
69+
70+
Precedence: **`space_id` / `TRACKIO_SPACE_ID` always wins** over `server_url` / `TRACKIO_SERVER_URL` when both are set (in code or in the environment). Trackio then behaves as if only the Space were configured.
71+
72+
Hugging Face–specific options such as `dataset_id` and `bucket_id` apply only when logging to a Space. When only `server_url` is in effect, configure persistence on the machine where the server runs (for example via `TRACKIO_DIR` on that host). See [Environment Variables](environment_variables.md).
73+
74+
## Related
75+
76+
- [Launch the Dashboard](launch.md) — CLI options, port, and remote access
77+
- [Environment Variables](environment_variables.md)`TRACKIO_SERVER_URL`, `TRACKIO_DIR`, and others

docs/source/track.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ trackio.init(project="my_project", name="tuned_run_1", group="tuned")
4444

4545
Runs with the same group name can be grouped together in sidebar, making it easier to compare related experiments. You can also group runs by any other configuration parameter (see [Tracking Configuration](#tracking-configuration) below).
4646

47+
### Remote logging (Hugging Face Space or self-hosted server)
48+
49+
By default, metrics are stored locally and you open the dashboard on your machine. You can instead send metrics to:
50+
51+
- A **Hugging Face Space**, by passing `space_id` (or setting `TRACKIO_SPACE_ID`). Trackio can create or reuse the Space and sync data there.
52+
- A **self-hosted Trackio server** (HTTP or HTTPS), by passing `server_url` (or setting `TRACKIO_SERVER_URL`). Use the write-access URL from `trackio.show()` (optionally with `write_token` in the query), or a base URL plus `TRACKIO_WRITE_TOKEN`. The client authenticates with the same **write token** the dashboard uses (not your Hugging Face token).
53+
54+
If both a Space and a self-hosted URL are configured (`space_id` / `TRACKIO_SPACE_ID` together with `server_url` / `TRACKIO_SERVER_URL`), **the Space takes precedence** and the self-hosted URL is ignored. Options such as `dataset_id` and `bucket_id` apply to Hugging Face deployments; when only `server_url` is in effect, configure storage on the host that runs the server (see [Environment Variables](environment_variables.md)).
55+
56+
For setup steps (running `trackio show`, binding to `0.0.0.0`, write tokens), see [Self-host the Server](self_hosted_server.md).
57+
4758
## Logging Data
4859

4960
Once your run is initialized, you can start logging data using the [`log`] function:

tests/e2e-local/test_api.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import asyncio
22
import tempfile
33
from pathlib import Path
4+
from urllib.parse import parse_qs, urlparse
45

56
import httpx
67
import pytest
78

89
import trackio
10+
import trackio.context_vars as context_vars
911
import trackio.utils as trackio_utils
1012
from trackio import Api
1113
from trackio.remote_client import RemoteClient as Client
@@ -199,6 +201,31 @@ def test_local_dashboard_supports_remote_client(temp_dir):
199201
app.close()
200202

201203

204+
def test_server_url_logs_to_self_hosted_server(temp_dir):
205+
project = "test_self_hosted"
206+
run_name = "self-hosted-run"
207+
208+
app, url, _, full_url = trackio.show(block_thread=False, open_browser=False)
209+
210+
try:
211+
write_token = parse_qs(urlparse(full_url).query).get("write_token", [None])[0]
212+
assert write_token
213+
214+
context_vars.current_server.set(None)
215+
context_vars.current_project.set(None)
216+
context_vars.current_run.set(None)
217+
218+
trackio.init(project=project, name=run_name, server_url=full_url)
219+
trackio.log(metrics={"loss": 0.5})
220+
trackio.finish()
221+
222+
client = Client(url, verbose=False)
223+
runs = client.predict(project, api_name="/get_runs_for_project")
224+
assert any(r.get("name") == run_name for r in runs)
225+
finally:
226+
app.close()
227+
228+
202229
def test_local_dashboard_returns_400_for_missing_required_parameter(temp_dir):
203230
app, url, _, _ = trackio.show(block_thread=False, open_browser=False)
204231

@@ -258,7 +285,10 @@ def test_local_dashboard_upload_api_accepts_only_server_uploaded_paths(temp_dir)
258285
blocked_target = trackio_utils.MEDIA_DIR / project / "files" / "blocked.txt"
259286
allowed_target = None
260287

261-
app, url, _, _ = trackio.show(block_thread=False, open_browser=False)
288+
app, url, _, full_url = trackio.show(block_thread=False, open_browser=False)
289+
write_token = parse_qs(urlparse(full_url).query).get("write_token", [None])[0]
290+
assert write_token
291+
write_headers = {"x-trackio-write-token": write_token}
262292

263293
try:
264294
with source_path.open("rb") as handle:
@@ -279,6 +309,7 @@ def test_local_dashboard_upload_api_accepts_only_server_uploaded_paths(temp_dir)
279309

280310
allowed_resp = httpx.post(
281311
f"{url.rstrip('/')}/api/bulk_upload_media",
312+
headers=write_headers,
282313
json={
283314
"uploads": [
284315
{
@@ -295,6 +326,7 @@ def test_local_dashboard_upload_api_accepts_only_server_uploaded_paths(temp_dir)
295326
)
296327
blocked_resp = httpx.post(
297328
f"{url.rstrip('/')}/api/bulk_upload_media",
329+
headers=write_headers,
298330
json={
299331
"uploads": [
300332
{
@@ -326,6 +358,8 @@ def test_local_dashboard_upload_api_accepts_only_server_uploaded_paths(temp_dir)
326358

327359
def test_local_dashboard_supports_mcp(temp_dir):
328360
pytest.importorskip("mcp")
361+
from mcp import ClientSession
362+
from mcp.client.streamable_http import streamable_http_client
329363

330364
project = "test_local_mcp"
331365
run_name = "mcp-run"
@@ -341,9 +375,6 @@ def test_local_dashboard_supports_mcp(temp_dir):
341375
)
342376

343377
async def check_mcp() -> None:
344-
from mcp import ClientSession
345-
from mcp.client.streamable_http import streamable_http_client
346-
347378
async with streamable_http_client(f"{url.rstrip('/')}/mcp") as (
348379
read_stream,
349380
write_stream,

0 commit comments

Comments
 (0)