Verlex documentation
Everything you need to run your code in the cloud with Verlex.
On this page
- Quick Start
- Installation
- Basic Usage
- Execution Modes
- Pricing & Billing
- Providers, Failover & Serverless
- How Verlex Runs a Job
- Files & Workspace Sync
- Dependencies
- Pre-warming
- Automatic Cloud Offloading
- Interactive Sessions
- AI assistants (MCP)
- Command Line
- Authentication
- Error Handling
- Troubleshooting
- Common GPUs
- Storage & Quotas
- Checkpoints & Spot Recovery
- Inference Endpoints
- Teams
- Spending Limits
Quick Start
Get your API key
Grab your API key from the dashboard.
Run
Run your code on a cloud GPU:
import verlex
# Define your function
def train_model():
import torch
model = torch.nn.Linear(100, 10)
# Your training code here...
return {"accuracy": 0.95}
# Run it on a cloud GPU - that's it!
result = verlex.cloud(train_model, gpu="A100", api_key="gw_your_key")
print(result)Expected output: {'accuracy': 0.95}
Installation
Install Verlex using pip (requires Python 3.10+):
pip install verlexBasic Usage
Pass Your API Key
Every function accepts api_key directly, no context manager needed:
import verlex
# Run in the cloud, one line
result = verlex.cloud(my_function, api_key="gw_your_key")api_key and the resource keywords are keyword-only, so always write them as name=value. There is no separate "mode" argument or test environment: the gw_live_ and gw_test_ prefixes only name the key type; every key runs against the live service with live billing.
Passing Inputs
Pass your function's inputs in args=[...] and its keyword arguments in kwargs={...}. Every other keyword configures the job and is never passed to your function. Inputs are checked against the function's signature before anything is uploaded or billed; the older form cloud(fn, [inputs]) still works when it is unambiguous and warns with the exact rewrite:
# No inputs
verlex.cloud(train_model)
# Positional inputs go in args=
verlex.cloud(square, args=[5])
verlex.cloud(train, args=[dataset, epochs])
# Keyword arguments for your function go in kwargs=
verlex.cloud(train, args=[dataset], kwargs={"epochs": 5})
# A parameter named like a job option (timeout, memory, ...) goes in kwargs=
verlex.cloud(fetch, args=[url], kwargs={"timeout": 5}, timeout=600)Specifying Resources
Every hardware field is optional, and only the fields you set are pinned. Whatever you leave out is sized from your code and chosen for cost and availability, so asking for a GPU and nothing else is the normal case:
# One GPU pinned, nothing else: cpu, memory and provider are chosen for you
result = verlex.cloud(fit, args=[data], gpu="A100")
# Pin more when you need it; each field is independent
result = verlex.cloud(fit, args=[data], cpu=4, memory="16 GB")
# Pin the cloud too
result = verlex.cloud(fit, args=[data], gpu="H100", provider="runpod")gpu_count puts several GPUs on one machine (up to 8, Performance plan). There is no multi-node execution: your function runs as one process that sees every GPU, so use them from your code:
def fit(data):
import torch
model = torch.nn.DataParallel(build_model()).cuda() # uses every GPU on the machine
...
# 8 GPUs on one machine (Performance plan)
result = verlex.cloud(fit, args=[data], gpu="H100", gpu_count=8, memory="512 GB")gpu= takes one GPU name. For providers, a list keeps price comparison and failover inside the list, while a single name pins hard and disables cross-cloud failover:
# A list keeps price comparison and failover inside it
result = verlex.cloud(fit, args=[data], gpu="A100", provider=["runpod", "gcp"])
# A single name pins hard: no cross-provider failover
result = verlex.cloud(fit, args=[data], gpu="A100", provider="runpod")Providers you can pin: aws, gcp, azure, verda, runpod, vast, jarvislabs, hyperstack, tensordock, lyceum, lambdalabs, beam, northflank, novita, cerebrium.
Values are checked in your process, before anything is provisioned: a misspelled option answers "did you mean gpu=?", and gpu="64 GB" tells you it belongs in memory=. Hardware is given only as keywords; the Hardware object was removed in 0.23.0.
The full set of job options:
result = verlex.cloud(
fit,
args=[data],
gpu="A100", # GPU type
cpu=8, # vCPU cores
memory="64GB", # memory
provider="aws", # pin to a cloud (omit to let Verlex pick the cheapest)
spot=True, # interruptible capacity
timeout=7200, # total seconds from submit to result
pip_packages=["numpy==1.26.4"], # extra packages, always installed
python_version="3.11", # match your local Python
)Execution Modes
One flag controls your price-speed tradeoff:
Performance fast=True
Fastest start
Best for time-sensitive workloads
Standard fast=False
Cheapest qualified machine
Best for batch jobs, cost-sensitive work
# Performance mode, fastest start
result = verlex.cloud(my_function, api_key="gw_your_key", fast=True)
# Standard mode (default), lowest price
result = verlex.cloud(my_function, api_key="gw_your_key")Performance mode (fast=True) requires the Performance plan ($10/mo). Standard mode (fast=False) is available on every plan. Both modes share one queue: Performance speeds up machine selection and launch, not queue position.
Pricing & Billing
Two parts: a monthly plan, then a per-job cost. Verlex passes the provider's live hardware price through at cost and adds a fixed service fee per GPU-hour, prorated per second. There is no percentage markup on the hardware. The Standard plan is free forever ($0/month); the Performance plan ($10/month) unlocks fast mode and 300 GB of storage.
The service-fee tier is derived from the hardware's FP16 tensor performance (TFLOPS):
| Tier | Example hardware | Standard (fast=False) | Performance (fast=True) |
|---|---|---|---|
| CPU (CPU-only jobs) | No GPU | $0.02/hr | $0.02/hr |
| Small (<185 TFLOPS) | T4, L4, A10, RTX 3090 | $0.10/GPU-hr | $0.20/GPU-hr |
| Mid (<600 TFLOPS) | A100, L40S, RTX 4090 | $0.30/GPU-hr | $0.45/GPU-hr |
| Large (<1500 TFLOPS) | H100, H200, MI300X | $0.40/GPU-hr | $0.60/GPU-hr |
| Flagship (≥1500 TFLOPS) | B200, B300, GB200, MI350X | $0.50/GPU-hr | $0.75/GPU-hr |
The service fee is charged per GPU-hour. There is no cold-start surcharge; a cold machine and a warm one cost the same.
- Per-second billing, 1 second minimum: you pay only for the time your job actually runs. Both the provider cost and the service fee are prorated to the second.
- Prepaid credits: buy credits in the dashboard (minimum top-up $10). Each job places a hold and settles the exact amount when it finishes.
- Auto top-up (opt-in): adds credits automatically with your saved card when the balance runs low (default: $50 added when it drops below $10).
- Funds-based execution: there is no runtime cap by default. A job runs until it finishes, your credits are exhausted, or it shows no CPU, GPU or output activity for 30 minutes. The optional timeout argument is the total time from submit to result, including queueing and provisioning; when it passes, the SDK cancels the job.
Providers, Failover & Serverless
Every job is priced across 11 clouds (AWS, GCP, Azure, Verda, RunPod, Vast.ai, JarvisLabs, Hyperstack, TensorDock, Lyceum, Lambda Labs) plus serverless container lanes for small jobs, and routed to the cheapest machine that fits.
import verlex
# Default: priced across 11 clouds, routed to the cheapest machine that fits
result = verlex.cloud(train_model, gpu="A100", api_key="gw_your_key")
# Provider list: cost comparison and failover stay inside your subset
result = verlex.cloud(
train_model,
gpu="A100",
provider=["runpod", "gcp", "verda"],
api_key="gw_your_key",
)
# Hard pin: one provider, no cross-provider failover if it is out of stock
result = verlex.cloud(train_model, gpu="A100", provider="aws", api_key="gw_your_key")Pinning: a single pinned provider disables cross-provider failover: if that cloud has no capacity, the job fails instead of moving. Prefer a provider list, which keeps cost comparison and failover inside your chosen subset.
Serverless: small jobs are routed to serverless container lanes (billed per second) when that is cheaper than a VM, or faster within budget on the Performance plan. This happens automatically; nothing to configure.
How Verlex Runs a Job
What happens between verlex.cloud(...) and the result. Numbers are the server's defaults.
Choosing a machine
Short, small jobs can run on serverless container lanes; anything with a workspace, several GPUs or a long runtime goes to a VM. Machines are ordered by price first, then by availability (a machine that just failed is skipped for 5 minutes), then by launch history. A pinned GPU is never substituted.
What you are charged for
Billing starts when the provider allocates the machine. A failure before that costs nothing; after it, the metered time is charged. Spot reclaims and failures caused by a Verlex redeploy are never charged.
Where your files live
Workspace inputs are stored by content hash. Outputs are kept on paid plans; on the free plan they are deleted once downloaded, or after 1 day. Checkpoints are deleted when the job ends. The full reference is docs/HOW_IT_WORKS.md in the repository.
Files & Workspace Sync
Send local files up to the VM before your job runs and bring generated files back when it finishes. Pass a Workspace to any cloud call. Auto mode mirrors your project root (respecting .gitignore) and returns whatever your code creates or changes; or list explicit Upload and Output specs when you want full control over what goes up and what comes back.
import verlex
from verlex import Workspace
def train():
from pathlib import Path
Path("runs").mkdir(exist_ok=True)
Path("runs/loss.json").write_text('{"loss": 0.42}')
Path("model.pt").write_text("weights")
return {"status": "done"}
# Auto mode mirrors your project root (respecting .gitignore) up to the VM,
# then returns new and changed files to the same paths locally
result = verlex.cloud(train, workspace=Workspace(), gpu="A100", api_key="gw_your_key")
# ./runs/loss.json and ./model.pt now exist locallyimport verlex
from verlex import Workspace, Upload, Output
def finetune():
import json
from pathlib import Path
cfg = json.load(open("config.json")) # uploaded and renamed below
Path("runs").mkdir(exist_ok=True)
Path("runs/epoch1.ckpt").write_text("weights") # trained on ./dataset
Path("model.pt").write_text("final weights")
return cfg["name"]
ws = Workspace(
root=None, # implied once uploads are listed; spelled out here for clarity
uploads=[
Upload("./configs/run.json", to="config.json"), # rename into the workspace
Upload("./dataset"), # whole folder, hierarchy kept
],
outputs=[
Output(path="model.pt"), # bring a single file back
Output(pattern="runs/*.ckpt"), # plus files matched by glob
],
)
result = verlex.cloud(finetune, workspace=ws, gpu="A100", api_key="gw_your_key")Your function runs inside the synced workspace, so relative paths like open('data/train.csv') work unchanged. Listing explicit Upload specs replaces the auto mirror — you get exactly the files you named, and nothing else; pass root='auto' if you want the project mirror alongside them. Explicit Output specs never affect what goes up. There is no hard size limit by default: Verlex warns once the workspace passes 100 MB, so set a ceiling with Workspace(max_size_bytes=...) if you need one. Import Workspace, Upload, and Output from the top-level verlex package.
Dependencies
Verlex scans your function's source, pins the versions of imported packages installed locally, and bundles local .py modules automatically. When you need certainty, pass pip_packages=: those packages are authoritative, always installed, and a failed install fails the job loudly instead of continuing silently.
import verlex
def train():
import numpy as np
import torch
from my_helpers import preprocess # local .py modules are bundled automatically
# ... your training code ...
return {"loss": 0.03}
# Auto-detect: imported packages are pinned to your local versions
result = verlex.cloud(train, api_key="gw_your_key")
# Authoritative installs: always installed, a failed install fails the job
result = verlex.cloud(
train,
api_key="gw_your_key",
pip_packages=["torch==2.3.1", "numpy==1.26.4"],
python_version="3.11", # 3.10, 3.11, 3.12, or 3.13
)python_version controls the interpreter on the cloud side: supported values are 3.10, 3.11, 3.12, and 3.13. By default Verlex matches your local interpreter.
Pre-warming
Kill the cold start before it happens. Call verlex.prewarm(your_function) and Verlex analyzes it, boots the right hardware, and hands you back a handle once the machine is ready (pass wait=False to get the handle immediately and let it boot in the background while your code runs). Calling the handle runs your function on that machine with no provisioning wait, and the machine stays attached to the handle between calls, so every call is warm. Release it with handle.release() or a with block when you are done; unreleased handles release themselves when your program exits, and a hard kill is covered by the server-side idle timeout.
import verlex
def train(batch):
import torch
...
wf = verlex.prewarm(train, gpu="A100", api_key="gw_your_key") # A100 starts booting NOW
data = load_and_clean() # local preprocessing runs meanwhile
result = wf(data) # runs train(data) on the warm machine
more = wf(other_data) # still warm, the VM stays attached
# Define inputs/outputs with a Workspace, just like verlex.cloud —
# it's a per-call argument on the warm handle:
from verlex import Workspace, Upload, Output
out = wf(data, workspace=Workspace(
uploads=[Upload("./configs/run.json", to="config.json")], # sync inputs up
outputs=[Output(pattern="checkpoints/*.ckpt")], # bring results back
))
wf.release() # recommended, not required: frees the machine now.
# If you skip it, the handle auto-releases when your
# program exits (atexit). A hard kill that skips atexit
# is covered by the server-side idle timeout instead.
# Or auto-release with a context manager:
with verlex.prewarm(train, gpu="A100") as wf:
result = wf(data)
# Scope mode: prewarm() with no function sizes hardware from THIS file's
# code and binds no function. You pass the function to run as the first
# argument on every call (workspace= stays a keyword, so it comes last):
wf = verlex.prewarm(gpu="A10") # no function -> scope mode
result = wf(train, data) # function is the first call arg
out = wf(train, data, workspace=Workspace()) # same, with a Workspace
wf.release()There are two ways to call prewarm, and they change how you invoke the handle:
| Bound mode | Scope mode | |
|---|---|---|
| How you call it | verlex.prewarm(train, ...) | verlex.prewarm() with no function |
| What the handle binds | A specific function, pinned to the warm VM. | No function; the calling file's code is analyzed to size hardware. |
| Invocation | wf(data) just its arguments | wf(train, data) the function to run is the first argument of every call |
| Where hardware comes from | The kwargs you pass (gpu=, cpu=, memory=, provider=) or the analysis of the bound function. | The same kwargs or the calling-file analysis. |
| Workspace argument | wf(data, workspace=Workspace()) | wf(train, data, workspace=Workspace()) the function stays first |
- workspace= works the same as on any other cloud call: it syncs inputs up before the function runs and brings generated outputs back. Because it is a keyword, its position never changes, and it is per-call, so different calls on the same warm VM can carry different Workspaces.
- Multi-GPU cluster jobs, spot jobs, and workloads that resolve to the serverless lane skip pre-warming; they are already routed optimally.
- If the warm session expires before you call, the handle still works and simply provisions normally.
Billing: you pay only for the time the warm machine actually exists at the real instance rate, never for analysis time, failed warm-ups, or skipped sessions. Idle time between calls is billed in segments; job runtime is billed as a normal job.
The machine is released by whichever of these comes first:
| Trigger | When |
|---|---|
| Idle timeout | After 10 idle minutes, counted from the moment the machine is ready; each call or status check resets the clock. |
| Lifetime cap | At 1 hour of total session lifetime. |
| Manual release | As soon as you call release() (or the with block exits). |
| Running job | Never mid-job: a job already running on the machine is not interrupted by these timers. |
Automatic Cloud Offloading
ExperimentalDon't want to manage when code runs in the cloud? Let Verlex decide automatically. When your system's CPU, memory, or GPU usage exceeds the threshold, heavy functions are transparently offloaded to the cheapest cloud provider.
import verlex
# Pass your API key directly
verlex.overflow(api_key="gw_your_key")
# Your code runs normally.
# When CPU, memory, or GPU exceeds 85%, functions go to the cloud.
data = load_data()
result = train_model(data) # system overloaded? → cloud
evaluate(result) # resources free → runs locallyParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| api_key | str | None | None | Your Verlex API key. |
| fast | bool | False | True = Performance mode (fastest start, higher per-GPU-hour fee). False = Standard mode. |
| threshold | float | 85.0 | CPU / memory usage percentage that triggers cloud offloading. Also used as the GPU threshold if gpu_threshold is not set. |
| gpu_threshold | float | None | None | GPU utilization or memory % that triggers cloud offloading. Defaults to threshold if not set. Requires pynvml and an NVIDIA GPU. |
| check_interval | float | 0.5 | Accepted but currently unused; the sampling interval is set with the VERLEX_OVERFLOW_SAMPLE_INTERVAL environment variable. |
| api_url | str | None | None | Override the Verlex API URL. |
| verbose | bool | True | Print offload activity and status to the console. |
Defaults: With just an API key, overflow monitors CPU, memory, and GPU. If any of them exceeds 85% usage, functions are automatically offloaded to the cloud in standard mode.
Note: GPU monitoring uses pynvml (included with the base install) and needs an NVIDIA GPU. Without one, overflow gracefully falls back to CPU and memory monitoring only.
CPU and memory monitoring need psutil. Install with: pip install 'verlex[overflow]'
Interactive Sessions
Use verlex.session() when a model or service should load once and handle several streaming turns on the same warm VM. The function starts serve_loop(), and each session.send() yields response chunks as they arrive.
import verlex
from verlex import serve_loop
def serve():
model = load_model() # loaded once per VM
def reply(prompt, history):
return model.stream(prompt) # str or an iterable of chunks
serve_loop(reply)
with verlex.session(
serve,
gpu="T4",
idle_timeout=90,
token_timeout=300,
) as session:
for token in session.send("Explain gradient checkpointing"):
print(token, end="", flush=True)Use it as a context manager. The VM stops and billing ends when the block exits or idle_timeout passes without input. ready_timeout limits the initial boot and model-load wait; token_timeout is a stall timeout that resets whenever the handler produces output.
Interactive sessions are experimental. verlex chat provides the equivalent terminal workflow. For independent function calls on one warm machine, use prewarm() instead.
AI assistants (MCP)
Connect Claude, Codex, Gemini CLI — or any MCP-compatible assistant — to your Verlex account and drive it in plain language: "run this fine-tune on an A100, show me the log as it goes, and kill it if the loss blows up".
Your assistant runs the connector locally and it talks to Verlex with your Verlex API key. Verlex never receives your Anthropic, OpenAI or Google credentials, and never sees your prompts.
Install
pip install "verlex[mcp]>=0.17.0"
# Storing your key once here means the configs below
# can carry no secret at all.
verlex loginConnect your assistant
Claude Code
claude mcp add verlex -- verlex-mcp
# View only: no submitting, cancelling or spending
claude mcp add verlex -- verlex-mcp --read-onlyClaude Desktop
Add this to claude_desktop_config.json — %APPDATA%\Claude\ on Windows, ~/Library/Application Support/Claude/ on macOS, ~/.config/Claude/ on Linux.
{
"mcpServers": {
"verlex": {
"command": "verlex-mcp",
"env": { "VERLEX_MCP_MAX_SPEND": "25" }
}
}
}Codex
[mcp_servers.verlex]
command = "verlex-mcp"
env = { VERLEX_MCP_MAX_SPEND = "25" }Gemini CLI
{
"mcpServers": {
"verlex": {
"command": "verlex-mcp",
"timeout": 60000
}
}
}Keep any client request timeout at 60 seconds or more: log tailing holds a request open for up to 25 seconds while it waits for new output.
What you can ask
Once connected, your assistant can do all of this on its own:
- →"What GPUs can I get, and what would an A100 cost me for two hours?"
- →"Run this script on an A100 and show me the output as it goes."
- →"Cancel that job if you see the loss go to NaN."
- →"List my last 10 jobs and what each one cost."
- →"How much have I spent this month, and how much credit is left?"
What it can reach
The connector covers identity, live GPU pricing, estimates, job submission and logs, cancellation, usage and billing, held GPU nodes, uploads and downloads, output artifacts, and inference endpoints (creating one requires an idle timeout and counts against the spend cap). With an explicitly enabled team-provisioned admin key it can also list team members and usage or invite a member. Fresh jobs stop billing when they end; held nodes stay warm and bill continuously until released.
Safety
The connector is a thin client holding your own API key, so it can only ever see and do what you can. There is no database access and no shared credential. On top of that:
- Anything that spends money or cancels work needs an explicit confirmation step, so it always surfaces as a visible action rather than a silent side effect.
- Every submission is priced first, and the estimate comes back with the job so you see the cost up front.
- A per-session spend cap (default $25), a one-GPU limit and a 60-minute duration limit are enforced before any request is sent.
- Job output is fenced and labelled as untrusted data, so instructions hidden inside a log cannot redirect your assistant.
Read-only mode
Add --read-only to the command and only the viewing tools are registered — nothing can be submitted, cancelled or spent. For a guarantee enforced by Verlex itself rather than by the connector, create the key with the jobs:read scope.
Command Line
The verlex command covers the same ground as the Python SDK. verlex --help and verlex <command> --help list every flag.
verlex login # save your API key (used by the SDK, CLI and MCP)
verlex run train.py --gpu A100 # run a script
verlex run train.py --gpu H100 --gpu-count 8 --provider runpod --provider gcp
verlex run train.py --spot --pip torch==2.3.1 --python 3.11 --fast
verlex jobs # recent jobs with full ids and cost
verlex status <job id> # status, machine, cost, error
verlex logs <job id> --follow # stream output
verlex cancel <job id>
verlex chat serve.py --gpu T4 --interactive
verlex deploy create MODEL --idle-timeout 1800
verlex deploy list # also: status, logs, stop, delete
verlex pool list # team data pool; verlex env for environments
verlex mcp serve # connect an AI assistantAuthentication
Get Your API Key
Sign up at verlex.dev to get your API key.
Using Your API Key
import verlex
# Pass api_key directly to any function
result = verlex.cloud(my_function, api_key="gw_your_key")
# Or use environment variable (VERLEX_API_KEY)
result = verlex.cloud(my_function) # picks up from envError Handling
Verlex raises specific exceptions so you can handle failures gracefully:
from verlex import (
VerlexError, # Base class for all Verlex errors
ConfigurationError, # Bad input or option, raised before submit (also a ValueError)
AuthenticationError, # Missing, invalid, frozen or paused key
InsufficientCreditsError, # Not enough credits, or no payment method
GPUUnavailableError, # Pinned hardware has no capacity right now
ProviderMaintenanceError, # Pinned provider is under maintenance
JobFailedError, # Your code or the platform failed the job
JobCancelledError, # The job was cancelled elsewhere
JobTimeoutError, # Your timeout passed
OutputDownloadError, # A required output could not be downloaded
SerializationError, # Function or result could not be (de)serialized
NetworkError, # Connection to Verlex API failed
RateLimitError, # Too many requests
)
import verlex
try:
result = verlex.cloud(fit, args=[data], gpu="A100")
except ValueError as e: # bad input, or a ValueError raised by your code
print(f"Bad input or ValueError on the machine: {e}")
except InsufficientCreditsError as e:
print(f"Need about {e.required:.2f} USD, have {e.available:.2f} USD")
except JobFailedError as e:
print(f"Job {e.job_id} failed ({e.remote_type}). Run: verlex logs {e.job_id}")
except JobTimeoutError:
print("Took longer than timeout= (which includes queueing and provisioning)")
except VerlexError as e:
print(f"Something went wrong: {e}")Troubleshooting
The errors people hit most, and what to do. Every error, its cause and whether you were charged is listed in docs/ERRORS.md in the repository.
| You see | What to do |
|---|---|
| ConfigurationError before anything runs | An input or option does not fit: fix args= / kwargs= or the option named in the message. Nothing was submitted or billed. |
| InsufficientCreditsError | Add credits or a payment method in the dashboard; the message says which one. |
| AuthenticationError: Verlex refused this API key | The key is paused or blocked, or the account is frozen; the message gives the reason. |
| GPUUnavailableError | The pinned hardware has no capacity right now. Pick one of the 5 alternatives printed above the error, or leave gpu= unset. |
| JobFailedError: ValueError: ... | Your code raised on the machine. The error is also a ValueError, so except ValueError works; verlex logs <job id> shows the full output. |
| JobTimeoutError ... total time since submit | timeout= counts queueing and provisioning too. Raise it or leave it unset. |
| JobCancelledError | The job was cancelled from the dashboard, the CLI or account controls. |
| OutputDownloadError | The job succeeded but a required output could not be downloaded. The return value is on .result and the files are still stored. |
| All providers failed to provision | Every fallback step failed. Retry later, relax the hardware, or replace a single-provider pin with a list. |
Common GPUs
Request a GPU by name with gpu="H100", and several GPUs on one machine with gpu_count=8. Verlex finds the cheapest available instance across 11 clouds, including AWS, GCP, Azure, Verda, RunPod, Vast.ai, JarvisLabs, Hyperstack, TensorDock, Lyceum, and Lambda Labs, plus serverless container lanes for small jobs.
| GPU | Memory | Best for |
|---|---|---|
| T4 | 16 GB | Inference, small training, budget-friendly |
| L4 | 24 GB | Inference, fine-tuning, video processing |
| A10 | 24 GB | Mixed inference and training |
| V100 | 16 GB | General ML training |
| A100 | 40/80 GB | Large model training, LLM fine-tuning |
| L40S | 48 GB | Large-scale inference and training |
| L40 | 48 GB | Inference, graphics, and fine-tuning |
| H100 | 80 GB | Largest models, fastest training |
| H200 | 141 GB | Maximum memory, cutting-edge workloads |
| B200 | 192 GB | Next-gen Blackwell, highest throughput |
If you don't specify a GPU, Verlex auto-detects your code's needs and picks the best option. Availability changes continuously; GET /v1/hardware/gpu-types is the source of truth for the currently schedulable public catalog and live price ranges.
Storage & Quotas
Job inputs, outputs, workspace files, and checkpoints live in Verlex object storage and count against your plan's storage quota:
Standard
25 GB of included storage.
Performance
300 GB of included storage.
Enterprise
1 TB+ of included storage: 1 TB by default, custom amounts on request.
Workspace upload blobs are stored by content hash and expire automatically after a period of non-use, so re-uploads of unchanged files are fast.
Checkpoints & Spot Recovery
Spot capacity is 60-80% cheaper than on-demand, but the provider can reclaim it at any time. Verlex handles the recovery: anything your job writes into the checkpoint directory is synced to object storage while it runs (every 60 seconds, every 20 seconds on short-notice providers, and immediately when a preemption notice arrives).
When a spot instance is reclaimed, the job is automatically re-enqueued on the next cheapest spot capacity (a different provider whenever one is stocked), the checkpoint directory is restored before your code runs again, and the reclaimed time is not billed. If spot keeps getting reclaimed, or no spot capacity fits the requested hardware anywhere, the job finishes on on-demand capacity so it always completes.
Write checkpoints, resume on restart
import os, pickle
def train():
# Set inside every Verlex job; synced to object storage while you run.
ckpt_dir = os.environ.get("GATEWAY_CHECKPOINT_DIR", "./ckpt")
state_file = os.path.join(ckpt_dir, "state.pkl")
start = 0
if os.path.exists(state_file):
with open(state_file, "rb") as f:
start = pickle.load(f)["epoch"]
for epoch in range(start, 100):
run_one_epoch()
tmp = state_file + ".tmp"
with open(tmp, "wb") as f:
pickle.dump({"epoch": epoch + 1}, f)
os.replace(tmp, state_file) # atomic: never syncs a torn fileFrameworks resume automatically
HuggingFace Trainer, PyTorch Lightning, and Keras jobs resume with no code changes: their checkpoint output is redirected into the synced directory and the latest checkpoint is picked up on restart. For plain Python state you can also use the injected helper: state = gw.resume() at the start, gw.checkpoint(epoch=epoch, total=total) each step.
The same restore also runs when an on-demand instance dies and the job is retried on fresh hardware. On providers with no advance reclaim notice, progress since the last periodic sync (at most the sync interval) can be lost.
Inference Endpoints
Point Verlex at a model on HuggingFace and get back an always-on HTTPS endpoint. The GPU is chosen from the model's own metadata, and Analyze reports the card, the disk and the hourly price before anything is provisioned — deploying is a separate, deliberate step, so the first time you learn what an endpoint costs is never the invoice.
Two kinds of endpoint
Decoder language models are served by a pinned vLLM behind an OpenAI-compatible API, so an existing OpenAI client works by changing base_url and nothing else. Tool calling works too: when a model's own chat template declares a tool-call format Verlex recognises, tools and tool_calls come back in the OpenAI shape, so agent frameworks run unmodified. It is detected from the template rather than guessed from the model family, and left off when the format is ambiguous; pass tool_call_parser to set it yourself, or "none" to turn it off. A model too new for the default vLLM release runs on a newer one instead — the analysis names which, and vllm_version lets you pin one yourself. Set max_replicas to let one endpoint grow onto more GPUs under load and shrink back when it is quiet; each replica is billed like the first machine. Every other supported task — embeddings, text and token classification, speech recognition, image classification, object detection, text-to-image and text-to-video — is served by a FastAPI app Verlex generates from a validated spec. Those expose POST /predict, and GET /schema describes exactly what that route accepts, so the URL you are handed documents itself. Video is the exception to answering in one call: a clip renders for minutes, so POST /predict answers at once with a job id and GET /result?id=… returns the MP4 once it is ready — poll it; it answers 202 with progress until then.
From Python or the CLI
import verlex
endpoint = verlex.deploy("Qwen/Qwen2.5-0.5B-Instruct", idle_timeout=1800)
print(endpoint.chat("Hello!")["choices"][0]["message"]["content"])
endpoint.stop()
# Same from the CLI:
# verlex deploy create Qwen/Qwen2.5-0.5B-Instruct --idle-timeout 1800
# verlex deploy stop <deployment id>A drop-in OpenAI base URL
from openai import OpenAI
# The endpoint the dashboard hands you, plus your ordinary Verlex key.
client = OpenAI(
base_url="https://api.verlex.dev/v1/deployments/dep_.../openai/v1",
api_key="gw_your_key",
)
answer = client.chat.completions.create(
model="my-endpoint",
messages=[{"role": "user", "content": "Explain spot instances."}],
)
print(answer.choices[0].message.content)System prompts without a redeploy
A system prompt set on a deployment is prepended by the control plane rather than baked into the machine, so changing it applies to the very next request — no restart, no cold start, no rebuild. A caller that sends its own system message keeps theirs rather than having both stacked.
Billed per GPU-second
An endpoint is always-on: it bills by the second from creation until you stop it, at the same per-GPU-hour rate as any other Verlex machine. There is no per-token pricing. Cold start is billed, because it is real GPU time, and stopping takes effect immediately: endpoint.stop(), verlex deploy stop, the dashboard, or DELETE on the deployment.
Bring your own app.py
For a model no built-in task fits, pass app_source with your own FastAPI app (and requirements, if it needs any) and it runs in place of the generated one — same machine sizing, same endpoint, same billing. A generated deployment's Source panel has a Use as custom app button, so the usual way in is to start from what Verlex generated and edit it.
- Define a module-level app: the server is started as uvicorn app:app.
- Serve GET /health, returning 200 once the model is loaded — it is how the machine reports ready. Return 503 with {"status": "failed"} if loading fails, and the deployment stops in seconds instead of waiting out the startup deadline.
- Serve POST /predict. It is the only inference route forwarded to a custom app; GET /schema is forwarded too, and worth serving so the URL describes itself.
- Read the model from the VERLEX_MODEL_REF environment variable rather than hardcoding one, so it always matches what the GPU was sized for.
- Requirements come from the same allowlist as generated apps. Anything off it is refused by name at create, not skipped — a skipped import would only fail later, on a machine already billing.
- The source is checked before any machine starts: it must parse, and it runs through the same acceptable-use scan as every job.
A gated repository needs a HuggingFace token when the deployment is created. It is used once to download the weights and is never stored, never logged and never returned by the API, so a replacement machine needs it supplied again.
Teams
Corporate accounts get organization-backed teams: a shared org credit pool, email invitations, and roles (owner, admin, member).
Admins allocate credits from the pool to members and can claw back unused allocations; members can also contribute personal credits to the pool. Everything is managed from the dashboard.
Spending Limits
Set daily or monthly spending caps from your dashboard to prevent unexpected charges. When a limit is reached, Verlex can block new jobs, warn you, or send a notification.
Block
New jobs are rejected until the next period or until you raise the limit.
Warn
Jobs still run, but you get a warning when approaching the limit.
Notify
You receive a notification when the limit is reached. Jobs continue.
Configure spending limits in your dashboard settings. You can also enable auto top-up to automatically add credits when your balance gets low.