torchrun loop, or anything you can launch with a command.
How it works
You supply four things:
Veri runs your entrypoint inside the base image on the GPU, with a set of
VERI_* environment variables injected:
Exit code
0 marks the job completed; a non-zero exit marks it failed.
When your script fails
A failed job’s error is your script’s own output: the exit status, then the last 20 lines your script wrote to stderr (up to 4 KiB). For example:137 means the container was killed for running out of memory, and the error says so: try a smaller batch or a bigger GPU shape. The error shows on the job page and in veri jobs get <id>; the full output (stdout and stderr) is in veri jobs logs <id>.
Keeping the job alive
The worker that runs your script tells Veri the job is alive at least once a minute for as long as the worker runs, whether or not your script prints anything or finishes a step. A quiet script (a long data load, a step that takes an hour, a run that never logs a metric) is not failed for being quiet. That report only says your script is still running. Progress and checkpoints have to come from your script:- Progress: append one JSON line per step to
$VERI_METRICS_FILE, for example{"step": 40, "loss": 1.21}, or callveri_sdk.log_metrics. The job’s current step and its curves come only from this file; without it the job shows no step. - Checkpoints: write
checkpoint-<step>/directories into$VERI_CHECKPOINT_DIRduring the run (see Checkpoints).$VERI_OUTPUT_DIRis captured only when your script exits with status0, so a run that dies midway keeps only the checkpoints it already wrote to$VERI_CHECKPOINT_DIR.
error.code worker_failed and terminates the instance. The error says how long the worker was silent and the last status and step it reported:
no step reported in place of the step means your script never wrote $VERI_METRICS_FILE. The job’s error.attribution is platform, and Veri reviews it for a credit.
Base images
Pick the base that matches your stack. Each prebakes the slow, compiled pieces so cold start stays fast; you layer light dependencies on top.Custom scripts run on NVIDIA GPUs only for now: the base-image catalog is CUDA-tier. A submit with an AMD
gpu_type (e.g. MI300X-192GB) returns a 400. AMD support is planned.Custom scripts run on AWS and DigitalOcean, not on Vast: Vast runs managed methods (GRPO, SFT, DPO) only, so
veri run-script --provider vast returns a 400 at submit, before any GPU is rented.Example: a minimal training run
A small project directory:train.py reads the Veri contract, trains, logs metrics, and writes a checkpoint:
train.py
requirements.txt layers your extra dependencies on top of the base image:
requirements.txt
$VERI_NUM_GPUS reaches the GPU machine instead of being expanded (to nothing) by your local shell.
For more than one GPU, start with --gpu-type L4-24GB --gpu-count 4 or --gpu-type A10G-24GB --gpu-count 4 on AWS (the default provider, 96 GB across four GPUs, usually on a box within two minutes). Use --provider digitalocean --gpu-type H100-80GB --gpu-count 1 only when a single GPU must hold 80 GB: its capacity comes and goes.
Multi-node
Multi-node gangs are not enabled yet — a
--num-nodes of 2 or more currently returns a “coming soon” 400 while the feature finishes hardware validation. Single-node (--num-nodes 1, the default) is unaffected.--num-nodes. Veri provisions all nodes together as a single unit, wires the topology into the VERI_* env above, and runs your entrypoint on every node. Your script drives the distributed launch (torchrun, with c10d rendezvous on rank 0).
Constraints:
- Whole-box only: each node is an 8-GPU box, so set
--gpu-count 8with--gpu-type A100-80GBor--gpu-type H100-80GB. Other shapes are rejected. - AWS only: multi-node runs on AWS.
- Custom script only: managed methods (GRPO, SFT, DPO) are single-node.
- Exact node count or fail:
--num-nodesmust be one of1,2,4,8,16. The gang launches at exactly that size or the submit fails fast (no silent cap-down).
torchrun at the injected topology. Rank 0 is the rendezvous host (VERI_HEAD_IP), every node reads its own VERI_NODE_RANK, and VERI_JOB_ID is the rendezvous id:
train.py
torchrun launch line, consuming the injected topology directly:
Checkpoints across nodes
Everything written underVERI_OUTPUT_DIR is captured, the same as single node. With N nodes you must avoid each rank writing the same path:
- Rank 0 saves (shown above): the simplest pattern. Gate the write on
rank == 0and use adist.barrier()so other ranks wait for the save to finish. - Sharded checkpoints: for large models,
torch.distributed.checkpoint(DCP) writes one shard per rank underVERI_OUTPUT_DIR, so no two ranks collide and you reload the shards on resume.
VERI_OUTPUT_DIR; a shared filesystem across nodes is not provided yet.
Observability
Everything shows up in the Veri UI for the job, alongside your managed jobs and deployments:- Live logs: your script’s stdout and stderr stream in real time.
- GPU metrics: utilization, memory, and power.
- Checkpoints: whatever you write under
VERI_OUTPUT_DIR. - Reward curves: two ways, pick either.
veri_sdk.log_metrics(shown above): append{"step": n, "reward": ..., "loss": ...}records and Veri renders them natively. Addveri-sdkto yourrequirements.txt.- Weights & Biases: connect your W&B account once (
veri wandb set) and Veri injectsWANDB_API_KEY,WANDB_PROJECT, andWANDB_RUN_IDinto the container, so your framework’s existingwandblogging flows to your own W&B account with no code change (OpenRLHF, verl, slime, and TRL all support it out of the box). Veri deep-links the run from the job page. AnyWANDB_*value you set yourself inenvtakes precedence.
Limits
- Node counts are exact:
1,2,4,8, or16. A gang launches at the requested size or the submit fails fast (no cap-down to fewer nodes). - Multi-node is whole-box, AWS, custom script only:
--gpu-count 8with--gpu-type A100-80GBorH100-80GBon AWS. Managed methods (GRPO, SFT, DPO) are single-node. - No shared filesystem across nodes yet: each node writes to its own
VERI_OUTPUT_DIR. Have rank 0 save, or shard withtorch.distributed.checkpoint. - Four base images (above). If your stack does not fit, a bring-your-own container image is the escape hatch (coming soon).

