Virtual Machines
Published
Updated
VMs are the lower-level infrastructure API. Use them when you need direct control over a running machine rather than a managed coding session.
Every VM boots from an environment. The environment manifest determines platform, sources, setup, services, ports, and simulator configuration. The public VM create endpoint accepts environment_id, optional snapshot_id, and optional boot-time environment variables; it does not accept a raw manifest.
Use the VM resource UUID from the create response's data.id in $VM_ID for VM detail, destroy, command, log, and simulator requests. A provider VM ID is not the resource UUID. A malformed ID, a valid UUID with no matching VM, or a VM in another account returns 404 with errors[].code set to not_found. Check the account and VM UUID before retrying.
Boot A VM
curl -s -X POST "$WALLFACER_API/accounts/$ACCOUNT_ID/vms" \
-H "Authorization: Bearer $WALLFACER_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: vm-smoke-test-001" \
-d '{
"environment_id": "'$ENVIRONMENT_ID'",
"env": {
"NODE_ENV": "test"
}
}' | jq ".data"The response is immediate. Boot continues asynchronously.
If the environment has a ready base snapshot, boot is fast. Otherwise the VM has to run setup fresh.
Wait For Readiness
Poll the VM until ready is true. Stop polling if the VM fails, stops, or is destroyed. A VM that was ready earlier can return ready: false after a terminal state; ready_at still records when it first became ready.
while true; do
vm=$(curl -s "$WALLFACER_API/accounts/$ACCOUNT_ID/vms/$VM_ID" \
-H "Authorization: Bearer $WALLFACER_TOKEN")
ready=$(echo "$vm" | jq -r ".data.ready")
status=$(echo "$vm" | jq -r ".data.status")
phase=$(echo "$vm" | jq -r ".data.phase // \"pending\"")
destroyed_at=$(echo "$vm" | jq -r ".data.destroyed_at")
echo "$status $phase ready=$ready"
[ "$ready" = "true" ] && break
if [ "$status" = "failed" ] || [ "$status" = "stopped" ] || [ "$destroyed_at" != "null" ]; then
echo "$vm" | jq -r '.data.error // "VM did not become ready"'
exit 1
fi
sleep 3
doneUse phase for progress display. Values roughly follow clone, setup, services, and ready, but clients should treat unknown values as opaque.
For macOS VMs, ready: true reports guest readiness. The host may still be checking SSH and setting up ports. Wait for the expected port_mappings before using public service URLs.
Use Exposed Ports
Services in the manifest can expose ports. Public URLs appear in port_mappings after the host completes port setup.
curl -s "$WALLFACER_API/accounts/$ACCOUNT_ID/vms/$VM_ID" \
-H "Authorization: Bearer $WALLFACER_TOKEN" \
| jq ".data.port_mappings"Each mapping includes a service name, port, protocol, and URL. Use names from the manifest instead of assuming port order.
Run Commands
Command execution is synchronous. The API returns exit_code, stdout, and stderr.
curl -s -X POST "$WALLFACER_API/accounts/$ACCOUNT_ID/vms/$VM_ID/commands" \
-H "Authorization: Bearer $WALLFACER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"command": "npm test",
"working_directory": "/workspace/my-api",
"timeout": 120
}' | jq ".data"Use direct command execution for smoke tests, diagnostics, one-off builds, or integration hooks. For AI coding, prefer tasks and sessions.
On macOS, the host records the VM address as soon as it resolves, during both fresh boot and snapshot restore. A command request after that point no longer fails with vm_no_ip solely because VM creation is still in progress. The command still needs a working SSH connection. If the address has not resolved, vm_no_ip includes the VM status, creation progress, and any recorded error. Check those details and the VM logs before retrying.
Pin A Snapshot
By default, a VM uses the environment's current base snapshot. Pass snapshot_id only when you need to pin a specific ready snapshot, such as for a rollback or reproducibility test.
{
"environment_id": "019d8e44-e110-71f6-864b-8e91470dcc4a",
"snapshot_id": "snap-019d8e44-a1b2"
}The snapshot must belong to the referenced environment.
Destroy VMs
Destroy direct VMs when you are done.
curl -s -X DELETE "$WALLFACER_API/accounts/$ACCOUNT_ID/vms/$VM_ID" \
-H "Authorization: Bearer $WALLFACER_TOKEN"While the host is reachable, Wallfacer flushes mounted storage before removing the VM. If the host was already removed or terminated after going offline, Wallfacer releases the VM without waiting for the unreachable host. Pending uploads and files left only on that host cannot be recovered. See VM Lifecycle.
Managed sessions handle their own VM teardown when they go idle or close. Direct VMs are your responsibility.