Lifecycle
VM lifecycle commands submit work to Sylve’s lifecycle queue. The initial response confirms that the action was accepted and provides a task ID. Inspect that task to determine whether the hypervisor operation eventually succeeded or failed.
Inspect the VM first
Section titled “Inspect the VM first”List all VMs or inspect a single RID:
doas sylve vms listdoas sylve vms get --rid 301Use JSON when a script needs the complete configuration and runtime timestamps:
doas sylve vms get --rid 301 --jsonThe interactive console uses a positional RID:
vms listvms get 301vms get 301 --jsonUnderstand lifecycle tasks
Section titled “Understand lifecycle tasks”All four VM actions return immediately after queuing a lifecycle task:
| Action | Direct CLI | Interactive console | Behavior |
|---|---|---|---|
| Start | sylve vms start --rid 301 |
vms start 301 |
Starts a powered-off VM. |
| Reboot | sylve vms reboot --rid 301 |
vms reboot 301 |
Requests a hypervisor reboot of a running VM. |
| Shutdown | sylve vms shutdown --rid 301 |
vms shutdown 301 |
Requests a graceful guest shutdown, then force-stops the VM if the configured wait expires. |
| Stop | sylve vms stop --rid 301 |
vms stop 301 |
Force-stops the VM without waiting for guest cooperation. |
Task status moves through queued, running, and then success or failed. A successful command submission does not by itself mean the VM action has finished.
Start a VM
Section titled “Start a VM”Start VM 301 and request a stable JSON response:
doas sylve vms start --rid 301 --jsonThe real response from Loki was:
{ "rid": 301, "action": "start", "outcome": "queued", "taskId": 26}The response records the requested action and task ID. It does not wait for bhyve startup to complete.
Follow the task
Section titled “Follow the task”Inspect the returned task:
doas sylve tasks get --id 26 --jsonLoki reported:
{ "id": 26, "guestType": "vm", "guestId": 301, "action": "start", "source": "user", "status": "success", "requestedBy": "console", "message": "completed", "error": "", "overrideRequested": false}The actual response also includes creation, start, update, and finish timestamps. Check status and error rather than assuming that a queued task succeeded.
List only active tasks for this VM:
doas sylve tasks active \ --guest-type vm \ --guest-id 301 \ --jsonAfter task 26 completed, the result was an empty array:
[]Reboot a running VM
Section titled “Reboot a running VM”Request a reboot with:
doas sylve vms reboot --rid 301 --jsonThe request on Loki produced task 27:
{ "rid": 301, "action": "reboot", "outcome": "queued", "taskId": 27}Reboot checks the VM’s replication ownership and PCI assignments before asking libvirt to reboot the domain. A failed preflight is recorded on the lifecycle task.
Choose shutdown or stop
Section titled “Choose shutdown or stop”Prefer graceful shutdown for a normally functioning guest:
doas sylve vms shutdown --rid 301 --jsonThe request returns a task immediately:
{ "rid": 301, "action": "shutdown", "outcome": "queued", "taskId": 28}While Sylve waited for the guest, task 28 appeared as running in tasks active. The installer did not shut itself down, so the configured ten-second wait expired and Sylve force-stopped the domain. The same shutdown task then completed successfully.
Use force-stop when the guest cannot shut down or when an active shutdown must be overridden:
doas sylve vms stop --rid 301 --jsonCalling stop for an already-stopped VM is safe and idempotent. Loki accepted task 29, completed it successfully, and left VM 301 powered off.
Review lifecycle history
Section titled “Review lifecycle history”Show recent VM tasks, optionally filtered to one RID:
doas sylve tasks recent \ --guest-type vm \ --guest-id 301 \ --limit 10The real sequence used for this guide was:
TASK ID GUEST GUEST ID ACTION STATUS CREATED───────────────────────────────────────────────────────────────────────────29 vm 301 stop success 2026-08-22T22:03:42Z28 vm 301 shutdown success 2026-08-22T22:03:29Z27 vm 301 reboot success 2026-08-22T22:03:17Z26 vm 301 start success 2026-08-22T22:03:02ZUse lifecycle commands in the console
Section titled “Use lifecycle commands in the console”The console places the task or VM identifier immediately after the leaf command:
vms start 301 --jsontasks get 26 --jsontasks active --guest-type vm --guest-id 301 --jsonvms reboot 301 --jsonvms shutdown 301 --jsontasks recent --guest-type vm --guest-id 301 --limit 10Do not include sylve or doas after entering sylve --console.
Wait for completion in a script
Section titled “Wait for completion in a script”Automation should retain the returned task ID and poll tasks get. Treat success and failed as terminal states:
result="$(doas sylve vms start --rid 301 --json)" || exit 1task_id="$(printf '%s\n' "$result" | jq -r '.taskId')"
while :; do task="$(doas sylve tasks get --id "$task_id" --json)" || exit 1 status="$(printf '%s\n' "$task" | jq -r '.status')"
case "$status" in success) break ;; failed) printf '%s\n' "$task" >&2 exit 1 ;; queued|running) sleep 1 ;; *) printf 'Unexpected task status: %s\n' "$status" >&2 exit 1 ;; esacdoneThis pattern distinguishes successful submission from successful execution and preserves the task’s error details when an operation fails.
Common failure conditions
Section titled “Common failure conditions”A lifecycle task can fail after it was queued. Common causes include:
- The requested RID does not exist.
- Another lifecycle or migration task is active for the VM.
- The node does not own the VM’s replication lease.
- A PCI device required for startup is unavailable or already in use.
- libvirt or bhyve cannot perform the requested transition.
- The hypervisor returns an error while requesting or monitoring a graceful shutdown.
Inspect the task’s error and message fields for the service’s precise failure reason. Configuration and hardware changes generally require a powered-off VM, so confirm task completion before continuing to the storage, networking, or configuration guides.