Skip to content

CLI reference

Terminal window
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).

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
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
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
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.

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.

Terminal window
# 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
Terminal window
# Everything the entity has.
metrana migrate wandb --entity my-team --workspace my-workspace --all-projects

Each 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-baseline
2026-08-19T09:15:32.902551Z INFO metrana.migrate.progress: 8 in flight (5 streaming, 3 initializing); longest my-team/sweeps/a1b2c3 initializing for 90s

The 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/a1b2c3
2026-08-19T09:14:02.884201Z INFO metrana.migrate.progress: planning my-team/sweeps/a1b2c3
2026-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.

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.

--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.

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 --float64 partway 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.