Tasks
Run a Python script or a whole project folder to completion on a matched GPU or CPU node — submit code, pick an image and hardware, stream the logs, and download the output files.
A task runs a Python script — or a whole project folder — once, to completion, on a node matched to the hardware you pick. You submit the code, an image, and the hardware — Meshive schedules it, streams the logs live, uploads whatever the program writes to /outputs, and charges your prepaid credit only for the time the container actually ran.
Script (main.py + optional requirements.txt) or Project (folder or .zip, uploaded to Assets) │ Submit — image + hardware + max run time ▼Runs on a matched node ──▶ logs stream live in the console │ program exits (or hits max run time) ▼Files in /outputs uploaded ──▶ download from the consoleEverything lives under Serverless › Tasks in your workspace console.
Tasks vs Serving
Section titled “Tasks vs Serving”Serving keeps a model resident behind an always-on endpoint. Tasks run a script and stop.
| Serving | Tasks | |
|---|---|---|
| What runs | A model behind an OpenAI-compatible endpoint | Your Python script, once |
| Lifetime | Until you pause or delete it | Until the script exits (or max run time) |
| You get back | API responses | Live logs + files from /outputs |
| Billing | GPU-hour × running replicas | Runtime of the container, from prepaid credit |
Typical task workloads: batch inference over a dataset, fine-tuning a model, data preprocessing — anything with a start and an end.
Quickstart
Section titled “Quickstart”-
Open Serverless › Tasks in your workspace console and click Submit task.
-
Name the task.
-
Add your code — the Script card has a Script | Project switch:
- Script — write one file in the code editor, or click Open file to load a
.pyfrom your computer. An optionalrequirements.txtgoes under the editor (see below). - Project — drop a project folder or a
.zipon the card, then pick the file to run (see Run a project).
You can also drop straight onto the Script card: one
.pyloads into the editor, and a folder or a.zipswitches the card to Project. Whatever you type under Arguments is passed to your program as command-line arguments. - Script — write one file in the code editor, or click Open file to load a
-
Input assets (optional) — attach assets from your workspace; each one lands at
/inputs/<name>. Files that aren’t in Assets yet can be uploaded right there: drop files or a folder on the Input assets card, or use Upload new in the Add assets dialog (see Upload and attach in one step). -
Pick an image — an official template, or your own image from a custom registry (see Container images).
-
Pick hardware — a GPU model and count, or a CPU-only preset:
Preset vCPU RAM Micro 2 8 GiB Small 4 16 GiB Standard 8 32 GiB Balanced 16 64 GiB Compute 8 16 GiB Compute L 16 32 GiB Compute XL 32 64 GiB Compute 2XL 64 128 GiB Compute 3XL 96 192 GiB Compute 4XL 128 256 GiB High-mem 8 64 GiB High-mem XL 16 128 GiB A preset with no node able to host it right now is shown as Unavailable.
-
Environment & secrets (optional) — available to your script as regular env vars. To bring in many at once, paste
KEY=VALUElines or drop a.envfile on the card (see Import environment variables). Your script can also read its task ID fromMESHIVE_SYS_TASK_ID(the olderMESHIVE_TASK_IDholds the same value for now). Names starting withMESHIVE_SYS_and a few other names Meshive sets in the container can’t be used — the form flags them as you type. Any other name, including otherMESHIVE_names, is yours to use. -
Max run time — 1 to 24 hours. The task is stopped when it hits this ceiling (see Task billing & refunds).
-
Completion webhook (optional) — get a signed POST when the task finishes (see Webhooks).
-
Click Submit task. The task card shows the live status, streaming logs, and Cost so far. While a project or an input asset is still uploading, the button stays off and the Run summary says what it’s waiting for — click it again once the upload has finished.
You can Stop a running task at any time — you’re charged for the time it actually ran, and no webhook event is sent for a manual stop.
requirements.txt
Section titled “requirements.txt”If you attach a requirements.txt, pip install -r requirements.txt runs inside the container, before your script starts.
In Project mode this editor is called Extra packages. Those are installed first, then the requirements file you chose for the project under Install requirements from (see Run a project).
Run a project
Section titled “Run a project”A single file is enough for many jobs. When your code is a folder — several modules, config files, a training repo — switch the Script card to Project and run it as it is, instead of pasting everything into one file.
Add a project
Section titled “Add a project”Drop a project folder or a .zip on the Script card, or click Choose folder or Choose .zip. A GitHub Download ZIP works as it is: the top-level folder inside it becomes the project name.
- One asset, uploaded for you. The console uploads the project to Assets as one asset and attaches it to the task as an input, so it counts toward the 20-input limit. The upload starts as soon as you drop, while you fill in the rest of the form.
- Submit waits for the upload. Submit task stays off until the upload has finished — it doesn’t submit by itself, so click it again afterward. If the upload fails, Retry is on the project row.
- It stays in Assets. The project is an ordinary asset in managed storage, billed as storage like any other upload until you delete it. Dropping the same folder again uploads another copy (
-2,-3, and so on). - Replace or Remove on the project row swaps or drops it. Switching between Script and Project keeps what you entered in both.
What the console leaves out. It tells you what it skipped under the project row, for example Skipped .git, __pycache__ (1,204 files).
| Left out | Why |
|---|---|
Version control, caches and tool folders — .git (and .hg, .svn), __pycache__ and *.pyc, node_modules, .ipynb_checkpoints, .mypy_cache, .pytest_cache, .ruff_cache, .tox, .nox, .idea, .vscode, *.egg-info | Rebuilt or irrelevant at run time, and often thousands of files |
Virtual environments — .venv, venv, and any folder that contains a pyvenv.cfg, whatever its name | Install packages with a requirements file instead |
Experiment logs — wandb, mlruns, lightning_logs | Output, not code |
Operating-system leftovers — .DS_Store, __MACOSX, ._*, Thumbs.db, desktop.ini | Noise |
.env and .env.* files (but not .env.example and the like) | Assets aren’t encrypted and are visible in Assets, so secrets stay out. The console offers to load them into Environment & secrets instead — see below |
Empty files, except empty .py files such as __init__.py (and py.typed, .gitkeep) | Assets can’t hold empty files; an empty __init__.py is kept because it makes a folder a package |
Other folders — data, build, dist, env — are uploaded like any other, so keep large datasets out of the project and attach them as input assets.
A .env file in the project. The console doesn’t upload it. Under the project row, Load into Environment & secrets reads it into the rows of that card (with several .env files, there is one Load button for each) and tells you where the variables went. Because the variables are then set in the container, code that calls python-dotenv’s load_dotenv() keeps working without the file.
File count. A task can fetch about 500 files in total at run time, counting the project and the files of the input assets you attach. Linked sources (Hugging Face, CivitAI, URLs) and bucket references aren’t counted. Over the limit, the console names the three folders with the most files and offers a Skip button for each. Many small files are better packed into one archive that your program unpacks.
A .zip is unpacked in your browser. One that is password-protected, uses non-UTF-8 file names, or unpacks to more than about 2 GiB can’t be opened that way — unzip it and drop the folder instead.
Choose what runs
Section titled “Choose what runs”Once the project is on the card, set:
- Run — the
.pyfile to execute. The console picks a top-levelmain.py,train.py,run.pyor__main__.pyfor you, the only candidate left once files likesetup.py,conftest.pyandtest_*.pyare set aside, or the only file with one of those names anywhere in the project (such asmypkg/train.py). When it can’t tell, the field is empty and the form asks you to Choose the file to run. - Install requirements from — a
requirements*.txtin the project. A top-levelrequirements.txtis chosen by default; choose None to skip installing. - Extra packages (optional) —
requirements.txtlines installed before the project’s own requirements file, for a package you don’t want to add to the repository. - Arguments (optional) — passed to the file as command-line arguments.
Under these fields the console shows the exact commands that will run, such as pip install -r requirements.txt followed by python -m mypkg.train. The Run summary’s Entrypoint row shows the same command.
How a project runs
Section titled “How a project runs”The project is downloaded like any input asset — that download time is billed — and then:
- It’s copied into a working folder, so the program can write next to its code. Very large files (~64 MiB and up) are linked read-only instead of copied, so big weights inside a project aren’t stored twice; they can be read but not changed in place.
- The working directory is the project folder, so relative paths such as
data/train.csvorconfigs/base.yamlwork, and sibling modules import as usual. This differs from Script mode, where the working directory is/outputs. - Requirements are installed. Extra packages first, then
pip install -r <file>from the project folder. Install time is billed, and a failed install ends the task as a user fault — see requirements.txt. - The entry file runs as
python path/to/file.py. When the file sits in a package — every folder from the project root down to it has an__init__.py— it runs aspython -m pkg.moduleinstead, so relative imports such asfrom .utils import loadwork, and sibling modules imported by plain name (import utils) are still found. A file at the top level always runs as a plain script, and__init__.pycan’t be chosen — add a__main__.pyto run a package. If your code starts Python again itself withpython -m(some multi-GPU launchers do), import sibling modules through the package (from pkg import utils) in those processes. - New files are collected. When the program exits, files it newly created inside the project folder are moved to
/outputsat the same relative path and uploaded with the rest of the outputs. Changes to files that came with the project aren’t collected. If moving them would pass the 200-file output limit, nothing is moved and the log lists what wasn’t saved.
Re-run and the task page
Section titled “Re-run and the task page”The task page shows the project instead of the code: its name with Open in Assets, the command, and the requirements file. Re-run with same config (and Re-run on the task list) attaches the same project and input assets again, after checking they still exist.
The project isn’t uploaded again, so edits on your computer aren’t picked up — drop the folder again to run the new version. If the project asset has been deleted since, the form says so and asks you to drop the folder again.
Import environment variables
Section titled “Import environment variables”Instead of typing each row in Environment & secrets, bring in a whole set of variables:
- Paste into a name field. Paste
KEY=VALUElines, JSON or YAML into the name of a row and it expands into one row per variable. - Paste into a value field. Paste a block of several
KEY=VALUElines. A single-line value is never split, so a value that contains=, like a base64 string or a URL, stays as you typed it. - Drop a file. Drop a file anywhere on the card.
- Click Import from file and choose a file (up to 1 MiB).
A line under the card sums up the result, for example “Imported 5 variables · 1 replaced · 2 marked secret · 1 line skipped”.
- Existing names are replaced. A name that is already a row gets the new value; the other names are added as new rows. If a name appears twice in the pasted text, the last one wins.
- Secret-looking names come in as Secret. A name containing
TOKEN(as a whole word, soMAX_TOKENSis left alone),SECRET,PASSWORD,API_KEY,ACCESS_KEY,PRIVATE_KEYorCREDENTIALis imported as a Secret. It’s a guess from the name only: click the Variable or Secret pill on a row to change it. - Values come in as written. Wrapping quotes are removed, but
${VAR},$(command)and%VAR%are not expanded. Numbers and booleans become text. - A file with several sets of variables asks which one you want. Two Compose services or two Kubernetes containers are never merged silently: pick one, or All, where a name that appears twice takes the later value.
- Kubernetes secrets are decoded. Values under
dataare decoded from base64 and come in as secrets, along withstringData.valueFromentries have no value to import, so they’re skipped.
The format is recognized from the content, so the file name doesn’t matter:
| Format | What’s understood |
|---|---|
.env | KEY=value, KEY: value, # comments, "double" quotes with \n escapes and multi-line values, 'single' quotes |
| Shell | export KEY=value (several on a line), .envrc, fish set -x, csh setenv |
| Windows | PowerShell $env:KEY = "value", set KEY=value, set "KEY=value", setx |
| JSON | An object, a list of {"name": …, "value": …} or "KEY=value" strings, comments and trailing commas (launch.json), AWS SAM env.json, ECS task definitions |
| YAML | Plain maps, Docker Compose, Kubernetes env, ConfigMaps and Secrets, GitHub Actions env, serverless.yml, App Engine env_variables |
| TOML | wrangler.toml, fly.toml, netlify.toml, Cargo [env], mise |
| Other | Java .properties, INI, systemd Environment=, Dockerfile ENV, two columns pasted from a spreadsheet |
- Lines that aren’t valid variable names are skipped and counted. The usual limits and reserved names still apply.
The same import works in a Pod’s or template’s Environment variables section: names that match a variable the template already defines fill in its value, and names that look secret are added as secret variables.
Container images
Section titled “Container images”| Source | Requirements |
|---|---|
| Official template | None — guaranteed to work with tasks out of the box |
| Custom registry image | Must have python (with pip, if you use requirements.txt) on the PATH |
A custom image without a working python fails at startup — that’s a user fault and is charged like any other user-code failure. Test the image locally first: docker run --rm <image> python --version.
Private registries use the registry credentials already connected to your workspace — the same ones used for custom pod templates.
Writing your script: outputs
Section titled “Writing your script: outputs”Anything your program writes to /outputs is uploaded automatically after the container exits and becomes downloadable from the task’s page in the console. In Script mode your script also starts with /outputs as its working directory; in Project mode the working directory is your project folder instead.
- Script mode: relative writes (
open("result.csv", "w")) already land in/outputs— no path handling needed. - Project mode: relative writes land in the project folder, and files that are new there are moved to
/outputswhen the program exits. Write to/outputs/...explicitly for anything that must survive a Stop or a max run time. _task.logis added automatically: a tail of the run’s log, archived alongside your files.- To keep only some of the files, choose them in the Output card of the submit form — see Choosing which files to save. By default everything is kept.
- When the run ends, everything in
/outputs(or only the files you chose to save) becomes an asset in your workspace — kept until you delete it, and billed as storage if it lives in managed storage. Each run becomes its own asset — re-running the task creates a new one (-2,-3, and so on). Only_task.logfollows the log retention policy. - The upload resumes after interruptions and retries automatically for up to ~3 days; its progress and state are shown on the task page. If you don’t need the files, Discard outputs on the task page stops the transfer and releases the storage.
| Output limit | Value |
|---|---|
| Max size per file | 5 GiB |
| Max number of files | 200 |
| Output disk budget | 20 GiB |
Choosing which files to save
Section titled “Choosing which files to save”In the Output card, click All files next to /outputs. Turn on any mix of Images, Videos, Model files and Data & logs, add your own patterns, or type a path under Check a path to see whether it would be saved. The example tree under /outputs marks what each choice keeps.
Your own patterns are globs, the same syntax as watched-folder include rules. Only files under /outputs that match are uploaded; the rest are deleted when the task ends. From code, pass the patterns as output_include (SDK and MCP) or --output-include (CLI).
| Pattern | Saves |
|---|---|
*.safetensors | Every .safetensors file, in any subfolder |
samples/*.png | PNGs directly inside samples/ — not in its subfolders |
samples/**/*.png | PNGs anywhere under samples/ |
checkpoints/ | Every folder named checkpoints, at any depth, with everything inside (/checkpoints/ only the one at the top) |
- Patterns are relative to
/outputs: writesamples/*.png, not/outputs/samples/*.pngor./samples/*.png. - A pattern that matches a folder saves everything inside it.
out/*also matches the folderout/sub, so it saves all ofout. - Matching is case-sensitive (
*.pngdoesn’t matchA.PNG). Brackets and braces ([abc],{png,jpg}) aren’t supported — add one pattern per alternative (*.png,*.jpg) and use?for a single character. Separate folders with/, not\. A leading!is reserved. - In the submit form you can type several patterns separated by commas. From code, pass a list (or repeat
--output-include) — there a comma is part of the pattern. _task.logis still added.- Files you don’t save still take up
/outputswhile the task runs — they count toward the output disk budget, and toward the disk charge where/outputsis billed as a volume. They are deleted, not uploaded, when the task ends. - If no file matches, the task still succeeds and no output asset is created; the task page shows that nothing matched the filter.
- The file-count limit applies to the files that match.
Local disk
Section titled “Local disk”A task gets the same disk a pod does: it’s ephemeral (wiped when the task ends) and split into two parts, both counted against one on-node budget.
- System storage — the container’s working space for package installs, model/HF caches, temp files, and logs. It’s sized automatically from your hardware, the same formula a GPU or CPU pod uses:
- GPU:
max(15 + 10 × GPU count, 5)GiB - CPU:
max(5 + 1 × vCPU, 5)GiB
- GPU:
/outputs— a separate 20 GiB budget for the files you want back (see above).
The submit form shows both numbers for your selected hardware (e.g. a single 24 GB GPU → 25 GiB system + 20 GiB /outputs = 45 GiB total).
Webhooks
Section titled “Webhooks”Add a webhook to be notified when a task reaches a terminal state:
| Event | When |
|---|---|
task.succeeded | Script exited with code 0 |
task.failed | Script failed — non-zero exit, OOM, eviction, install failure, or a platform fault |
task.timed_out | Max run time reached |
task.outputs_ready | Every output file has finished saving to Assets. Fires after the terminal event — outputs can still be uploading when task.succeeded arrives. |
Terminal states only (plus task.outputs_ready once the outputs land). A manual Stop sends no event.
The payload envelope matches inference webhooks:
{ "id": "evt_3f9c1a2b4d5e6f70", "type": "task.succeeded", "created_at": 1780000000, "data": { "id": "task_abc123", "name": "nightly-batch-embed", "status": "succeeded", "fault_class": "none", "exit_code": 0, "failure_reason": null, "namespace_name": "my-workspace", "gpu_model": "RTX 4090", "created_at": 1779996400, "finished_at": 1779999998, "total_cost": "0.42000000" }}fault_class—none(succeeded),user, orplatform. This is what decides whether anything is refunded.total_cost— the final, trued-up charge in credit.- Terminal payloads also carry
output_upload_state— where the output transfer stands at that moment. Wait fortask.outputs_readybefore fetching files programmatically.
Deliveries carry the same X-Meshive-Signature HMAC header as every other Meshive webhook — verify it exactly as described in Webhooks → Verifying the signature. Retries, idempotency (dedupe on the event id), and security rules are also identical.
No webhook, or one that can’t be reached? You get an email. When a task has no webhook for its event — no webhook_url on the task and no workspace endpoint subscribed to that event — the person who submitted it gets one email when it succeeds, fails, times out, or is stopped because the credit balance ran out. If the task has webhooks but none of them accepted the event after all retries, the same email goes out with the reason the delivery failed. A task you stop yourself sends no email.
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Max run time | 1 – 24 hours |
Script + requirements.txt size | ~960 KiB combined (a project’s files are not counted — they’re uploaded to Assets) |
| Input assets | Up to 20 per task; a project counts as one |
| Files fetched at run time | ~500 across the project and its input assets |
| Environment variables | Up to 64, ~32 KiB total |
| GPUs per task | Up to 8 |
| Concurrent tasks | No cap — every task is paid |
| Output files | 5 GiB / file · 200 files · 20 GiB total |
- Task billing & refunds — when the meter starts and stops, the fault table, and what gets refunded.