Skip to main content
Managed training on Veri is dataset + base model → trained checkpoint. You submit a training job; Veri provisions the GPU, runs the loop, streams metrics, and uploads the result. You either download the checkpoint or deploy it directly. The training method you pick decides how outputs are scored. GRPO takes a reward function; SFT and DPO learn directly from examples or preference pairs. For the smallest end-to-end run, see the Managed GRPO quickstart — one decorated reward and one command.

Methods

GRPO is the default. All methods support LoRA and QLoRA.
grpo_agentic is being rebuilt and is not currently runnable as a managed method. To run multi-turn, environment-based RL today, use a custom script with your own framework (for example verl) and environment.

The training loop

What happens at each transition:
  • queued: job row written (HTTP 402 at submit if the workspace has no billing account). No spend yet. Veri tries to acquire a GPU instance from here; if no zone has capacity, the job goes straight to failed with CAPACITY_UNAVAILABLE. On GCP the job stays queued for up to about 15 minutes while Veri waits for Flex-start capacity, and longer when the one GCP GPU is in use (see GCP).
  • provisioning: Veri has leased a GPU and the instance boots. started_at is still null. Once the instance is acquired, the GPU is rented and cost accrues from this point, even before the first training step. If you cancel the job before it reaches running, that cost is waived and the job is not charged (see Training job charges). A job that never secures a GPU (for example CAPACITY_UNAVAILABLE) rents nothing and is not billed.
  • configuring: the instance is up and the worker is setting up its environment.
  • running: the worker has started the training process (dataset and model load, then training steps). started_at populates. Metrics start streaming with the first step.
  • completed | failed | cancelled: pod auto-terminates. Cost is attributed (cost_attributed_at). Stripe credit is deducted (billing_deducted_at). The full stdout is uploaded to S3 and available via GET /v1/training_jobs/{id}/logs/full.
Cancellation is immediate: the instance is terminated as soon as the request is accepted. The worker does not get a chance to flush anything, and a job saves its weights only at completion (see Checkpoints), so a cancelled job leaves no trained weights.

Failure modes

When a job ends in failed, error.code carries one of: error carries code, message and attribution (there is no log_tail field). For triage, read the last worker lines in the dashboard’s Logs tab or fetch the full stdout, uploaded every 10 seconds and retained after the job ends, via GET /v1/training_jobs/{id}/logs/full.

Who the failure belongs to

A failed job also carries error.attribution: Credits appear on the job as refund with status (approved, credited, pending or denied) and amount_usd, and as a job_refunded entry in the job’s events. Multi-GPU jobs run a hardware check before any of your code and after a failure; the result is what makes a hardware_error verdict, never text in the log.

Checkpoints

Every managed job saves its trained weights as a final artifact when training completes. Managed jobs can also save intermediate checkpoints while they run, so a job that fails or is cancelled at hour 19 of 20 still leaves weights (and the optimizer state) to download or to continue training from with resume_from. See Checkpoints and resume for the knobs (checkpoint_every, checkpoint_keep), what a checkpoint holds, retention, and how to resume.

Submitting a job

wait() streams the job’s logs to your terminal while it polls: provisioning, installs, and training steps appear live with no extra flags. Streaming is best-effort: if the log stream drops, wait() keeps polling silently and still returns the finished job. Pass stream=False for quiet polling only. You can also tail any job from another terminal:
A job that has not written any log yet prints [no logs available yet] and exits 0. A finished job whose log was never uploaded fails instead, with log was never uploaded for this job and a non-zero exit, since no log will ever arrive for it. download() fetches the complete Hugging Face checkpoint — weights plus adapter config and tokenizer files — into ./checkpoints/<job_id>/, a directory you can load directly with from_pretrained(...) or push to the Hugging Face Hub.

Choosing a launch region

Capacity is per-region. If your job fails with CAPACITY_UNAVAILABLE, another region may have the GPU you need right now:
Pass region="us-west-2" to client.training_jobs.create or the @training_job decorator, or run veri train --region us-west-2. Omit it to launch in the default region. Unknown or not-yet-available regions are rejected at submit with the list of valid options.

Choosing a GPU provider

Veri supports multiple GPU providers: AWS (NVIDIA, default), Vast.ai (NVIDIA marketplace), DigitalOcean (H100 droplets), and GCP (a single A100 80GB or H100 80GB). Pass provider at submit time to choose where your job runs:
See GPU providers for the full provider list, GPU types, pricing, and availability checks.
This snippet uses the low-level client and assumes you have already uploaded a dataset (the dataset it references) and have a reward file on disk; the reward source is attached to the job inline. For a runnable end-to-end version with @training_job + veri train, see the Managed GRPO quickstart.
Same shape from the CLI:
with kind = "train" in the TOML config. See the CLI training reference for the full schema.

Where to go next

Managed GRPO quickstart

End-to-end on a small model: one decorator, one command.

Deploy your checkpoint

Skip downloading — serve from Veri with an OpenAI-compatible API.

Track it with W&B

Live loss and reward charts for your run.

CLI training commands

veri run, veri jobs, reward files.