CLI reference
metrana migrate <source> [options]wandb is the only source today. The metrana command itself needs no extra; each source’s subcommand needs
its own (see Installation).
metrana migrate wandb
Section titled “metrana migrate wandb”Source coordinates
Section titled “Source coordinates”| Argument | Default | Meaning |
|---|---|---|
--entity |
required | W&B entity (team or user) owning the project |
--project |
required | W&B project to migrate from. Repeatable, unless --all-projects is given |
--all-projects |
off | Migrate every project under the entity. Cannot be combined with --project |
--run RUN_ID |
every run | Migrate only this run id. Repeatable; needs a single --project |
--filter JSON |
none | W&B MongoDB-style query filter, as a JSON object, applied to every selected project |
--wandb-api-key |
WANDB_API_KEY, then the stored wandb login |
Source API key |
Target coordinates
Section titled “Target coordinates”| Argument | Default | Meaning |
|---|---|---|
--workspace |
required | Target Metrana workspace |
--to-project |
the source project’s name | Target Metrana project. Created if missing. Only with a single --project; across several, each keeps its source name |
--api-key |
METRANA_API_KEY |
Metrana API key |
--ingestion-url |
METRANA_INGESTION_URL, then production |
Metrana ingestion endpoint |
Behaviour
Section titled “Behaviour”| Argument | Default | Meaning |
|---|---|---|
--float64 |
off | Migrate float points at float64 instead of float32. Integer-valued series take float64 regardless |
--always-append-id |
off | Suffix every run name with the source run id, not just contested ones |
--workers N |
cores, capped at 8 | Runs migrated concurrently, one worker process each. 1 stays in this process |
--page-size ROWS |
10000 |
History rows fetched from W&B per request |
--flush-every ROWS |
50000 |
Source rows between durability checkpoints |
--verbose |
off | Log every stage each run passes through, not just its start and outcome |
--heartbeat SECS |
30 |
Seconds between in-flight status lines. 0 turns them off |
METRANA_LOG_LEVEL |
INFO for this command |
Log level. Env var, not a flag; overrides the command’s default |
--dry-run |
off | Resolve runs and names and report; log nothing |
Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
0 |
Every selected run migrated, or a dry run completed |
1 |
At least one run failed, or the source extra is not installed |
2 |
Usage error — a missing or unusable argument, or a malformed --filter |
A failed run is reported with its error and left resumable: it is not closed, so re-running the same command continues it. One run failing does not stop the others.
Migrating many runs at once
Section titled “Migrating many runs at once”Runs migrate concurrently, one worker process per run, so a project of a thousand runs is not a thousand sequential downloads. Processes rather than threads because decoding history rows is interpreter work that threads can only interleave, never overlap.
# Every run in three projects, twelve at a time.metrana migrate wandb --entity my-team \ --project sweeps --project ablations --project baselines \ --workspace my-workspace --workers 12# Everything the entity has.metrana migrate wandb --entity my-team --workspace my-workspace --all-projectsEach source project migrates into a Metrana project of the same name, created if missing. Progress is logged as each run lands, in completion order — a short run is never held up behind a long one.
Two knobs decide throughput:
--workers— how many runs are in flight. The default is one per core up to 8. Raising it past what W&B will serve you concurrently gains nothing, and each worker carries its own client and ingestion engine, so memory is the other bound.--page-size— history rows per request to W&B. Every page is a round trip, so the default of 10000 is far above the client’s own; lower it if a run’s rows are wide enough to make a page unwieldy.
Watching a migration that is still running
Section titled “Watching a migration that is still running”A run only produces its report once it is over, so while runs are in flight the workers report the stage each one has reached. Events cross back to the parent and one thread there logs them, so output stays readable however many workers are running.
Each stage is announced as it is entered, not when it finishes — so the stage a run stalls in is the one left on screen:
| Stage | What the run is doing | What a long wait there means |
|---|---|---|
opening |
Re-resolving the run against the source API in its worker | The source API is slow or rate-limiting |
planning |
Enumerating the run’s history keys to build the name mapping | A long run whose key metadata is unavailable, so keys are being scanned |
initializing |
The ingestion handshake — creating or resuming the target run | See When a run stalls below |
attributes |
Writing config, summary, provenance, tags and description | Rarely slow; a lot of config entries |
streaming |
Pulling history pages and logging points | The normal working state — where a healthy migration spends its time |
checkpoint |
A durability barrier: blocks until everything buffered is acknowledged | Ingestion backpressure, or a large --flush-every |
closing |
Final flush and close, bounded by the close timeout | Outstanding data still being acknowledged |
Everything goes through loguru in the SDK’s own format, so migration lines,
SDK lines, and the ingestion engine’s lines interleave as one log — and adding a sink captures the whole
migration to a file. The command reports at INFO, rather than the SDK’s quieter library default, and defers
to METRANA_LOG_LEVEL whenever that is set.
By default each run logs when it starts, and a heartbeat every --heartbeat seconds says what is in flight:
2026-08-19T09:14:02.118374Z INFO metrana.migrate.progress: start my-team/sweeps/a1b2c3 -> wandb-baseline2026-08-19T09:15:32.902551Z INFO metrana.migrate.progress: 8 in flight (5 streaming, 3 initializing); longest my-team/sweeps/a1b2c3 initializing for 90sThe heartbeat names the run that has been in its stage longest, which is what turns a migration that has
stopped moving into a line you can act on — the same run and stage repeating across heartbeats is the signal.
--heartbeat 0 turns the periodic line off without silencing the per-run start lines.
--verbose logs every stage transition for every run instead of just the starts. That is one line per run per
stage, so it is worth reaching for on a handful of runs and not on a thousand:
2026-08-19T09:14:02.118374Z INFO metrana.migrate.progress: opening my-team/sweeps/a1b2c32026-08-19T09:14:02.884201Z INFO metrana.migrate.progress: planning my-team/sweeps/a1b2c32026-08-19T09:14:03.220518Z INFO metrana.migrate.progress: initializing my-team/sweeps/a1b2c3 (42 keys)2026-08-19T09:14:04.907733Z INFO metrana.migrate.progress: streaming my-team/sweeps/a1b2c3 (from step 0)2026-08-19T09:15:11.443090Z INFO metrana.migrate.progress: checkpoint my-team/sweeps/a1b2c3 (50000 points)The two are independent: --verbose decides how much each run says, --heartbeat how often the migration as
a whole reports in. Both are pure reporting and neither changes what is migrated.
When a run stalls
Section titled “When a run stalls”A run parked in initializing is waiting on the ingestion handshake, which has its own 120s timeout — so if
it does not clear, the run fails with Init timeout exceeded and is left resumable.
Resuming a run is the slower path: an existing run’s handshake waits for every ingestion partition the run
covers to flush before it reports ready, where a newly created run has nothing to wait for. A migration that
is re-run to continue takes that path for every run, so several runs sitting in initializing at once points
at the ingestion workers rather than at the migration — check that they are up and consuming, and that no
partition is lagging.
A run parked in checkpoint or closing is waiting on acknowledgement of data already sent, which is
backpressure rather than a stall, and usually clears on its own.
One run failing does not disturb the others: it is recorded, left resumable, and the workers carry on. That holds even if a worker is killed outright — its run is reported as failed and re-running the command picks it back up.
What a checkpoint does
Section titled “What a checkpoint does”--flush-every sets the row interval between checkpoints. At each one, everything buffered is sent and the
migration blocks until the data is acknowledged — the same durability barrier
metrana.flush() provides. It bounds how much
work an interruption repeats, at the cost of a round trip. There is a final checkpoint at the end of every
run, whatever the interval.
Resuming
Section titled “Resuming”There are no resume flags, because resuming is not a mode: re-run the identical command and each series continues from the last step already in Metrana. Nothing is written locally, so a migration can be resumed from a different machine.
Two consequences worth knowing:
- Re-running a completed migration is safe but not free — it re-opens each run to find nothing to do.
- Do not change
--float64partway through. It sets the run’s storage precision, so a resumed migration logging at a different precision leaves one run holding both. Integer-valued series are unaffected: they pin float64 from their own first point, so they stay exact and consistent either way.