Guides
Running commands
Run a command in a machine, wait for its result or stream its output as it arrives, and handle exit codes, timeouts, and retries.
POST /v1/machines/{id}/exec runs one command in a running machine and returns its exit code and output. The command is an operation, so it gets an idempotency key and runs after any earlier operation on the same machine. Past commands and their output stay in the machine's command history.
Run and wait
vy exec dev -- uname -a # argv, run directly
vy exec dev -c 'cd /work && make test' # a shell commandSend exactly one of:
argv: the program and its arguments, run directly without a shell.command: a string run as/bin/sh -c <command>, at most 8 KiB.
In the SDKs a string becomes command and an array becomes argv. In the CLI, words after -- are argv and -c is command. On a machine with an environment, commands run with its variables loaded.
The result
The finished operation's exec_result holds:
| Field | Meaning |
|---|---|
exit_code | The command's exit status; null if it didn't exit normally. |
stdout_base64, stderr_base64 | The output, base64-encoded. |
timed_out | It hit timeout_ms and was killed. |
cancelled | It was cancelled. |
truncated | Output went over 1 MiB and was cut. |
The operation succeeds only when the command exits with code 0. Otherwise it ends failed, still carries exec_result with the output, and its error_code says why:
error_code | Meaning |
|---|---|
exec_nonzero | The command exited with a non-zero code. |
exec_timed_out | It ran past timeout_ms. |
exec_cancelled | It was cancelled. |
exec_no_exit_status | It ended without an exit status, for example killed by a signal. |
The SDKs raise OperationError for a failed exec; its operation.exec_result holds the output. The CLI exits with the remote exit code, 124 if the command timed out, and 125 if VMYard couldn't run it. A failed exec also emits the operation.failed webhook.
import { OperationError } from "@vmyard/sdk";
try {
await machine.exec("make test");
} catch (error) {
if (error instanceof OperationError && error.errorCode === "exec_nonzero") {
console.log("tests failed", error.operation.exec_result?.exit_code);
} else throw error;
}Timeouts
timeout_ms is 1 to 600,000 (10 minutes) and defaults to the maximum. A larger value is refused with 422 timeout_exceeds_runtime_limit. When the timeout passes, the command's process group is killed. For anything longer, use a background command.
Stream output
Set stream: true to get output while the command runs. The response is 200 application/x-ndjson: one JSON object per line.
vy exec dev -- npm test # streams by default; --no-stream waits instead{"type":"started","operation_id":"6f1c…","operation_url":"/v1/operations/6f1c…"}
{"type":"stdout","data_base64":"PiB0ZXN0Cg=="}
{"type":"stderr","data_base64":"d2FybmluZwo="}
{"type":"exit","operation_id":"6f1c…","status":"succeeded","exit_code":0,"timed_out":false,"cancelled":false,"truncated":false}The stream starts with started and ends with exactly one exit or error frame. stdout and stderr frames are a preview; the exit frame and the operation's exec_result are the authoritative result.
- If the connection drops, the command keeps running. Send the same idempotency key and body again to stream it from the start, or follow the operation from the
startedframe. - Output is kept with the operation, up to 1 MiB per operation, and also appears as
stdout/stderrevents onGET /v1/operations/{id}/events. - An
errorframe (code,message) means VMYard couldn't run or finish the command.
Retries and ordering
Retrying with the same idempotency key and body returns the original operation; it never runs the command twice. A different body with the same key is 409 idempotency_conflict. Commands on one machine run one at a time in the order they were accepted.
Who can run commands
Owners, Admins, and Operators, or an API key with at least the use_existing preset. The machine must be running.