# Lifecycle states

> Desired and observed machine states, the actions that move between them, and how to read a machine that is in between.

A machine has two states. `desired_state` is what you asked for most recently. `observed_state` is what VMYard last confirmed on the machine's host. They differ while an operation is in flight, and the observed state is the one to trust.

## Desired state

| Value | Set by |
| --- | --- |
| `running` | Create, start, or restart. |
| `stopped` | Stop, including an [auto-stop](/docs/concepts/auto-stop), [idle sleep](/docs/concepts/idle-sleep), or a stop because the balance ran out. |
| `deleted` | Delete. |

## Observed state

`observed_state` is an object with a `state` field:

| `state` | Meaning |
| --- | --- |
| `absent` | No machine exists on a host yet, or it has been removed. |
| `provisioning` | The machine is being created. |
| `running` | It is up. Also carries `boot_id`, which changes on every boot. |
| `stopped` | It is stopped. Nothing is metered. |
| `degraded` | The host reported a problem with the machine. |
| `unknown` | VMYard could not confirm the machine's state. |

```json
{
  "desired_state": "running",
  "observed_state": { "state": "running", "boot_id": "c1a4e7f0-…" }
}
```

## Actions

| Action | Route | CLI | Result when it succeeds |
| --- | --- | --- | --- |
| Create | `POST /v1/machines` | `vy new` | Observed `running`. Emits `machine.ready`. |
| Stop | `POST /v1/machines/{id}/stop` | `vy stop` | Observed `stopped`. Emits `machine.stopped`. |
| Start | `POST /v1/machines/{id}/start` | `vy start` | Observed `running`. Emits `machine.started`. |
| Restart | `POST /v1/machines/{id}/restart` | `vy restart` | Observed `running` with a new `boot_id`. Emits `machine.started`. See [Restart](#restart). |
| Delete | `POST /v1/machines/{id}/delete` | `vy rm` | The machine is confirmed gone. Emits `machine.deleted`. |

Each action is an [operation](/docs/concepts/operations) and needs an `Idempotency-Key`. When a lifecycle operation ends `failed` or `correlation_miss`, VMYard emits `machine.error`; see [Webhooks](/docs/guides/webhooks).

Creates, starts, and restarts also pass billing admission: the balance, the spend cap, the trial rules, and the concurrency and start-rate limits. See [Usage, billing, and limits](/docs/guides/billing#limits).

## Restart

A restart is one operation that reboots a running machine in place: every process in it ends, `/work` is kept, and it comes back ready. On VM hosts the VM reboots from its own disk; on Docker hosts the workload's processes end. Exposed ports, the SSH port, and the [egress policy](/docs/guides/networking) stay.

The machine never stops: its metered run stays open, no `machine.stopped` is sent, and its [auto-stop](/docs/concepts/auto-stop) deadline starts again. A retry with the same `Idempotency-Key` never reboots twice.

It succeeds only when the machine is running and ready with a `boot_id` different from the one before. A restart that kept the old `boot_id` ends `correlation_miss`; the machine stays running and usable. A host that can't restart its machines yet ends the operation `failed` with `restart_unsupported`: nothing changed and the machine kept running.

## Reading a machine in between

- **Desired `running`, observed `provisioning`:** a create is in progress. Wait for its operation.
- **Desired `stopped`, observed `running`:** a stop is queued or running.
- **Observed `degraded` or `unknown`:** don't assume the machine works. You can still delete it. Check its latest operation's `error_code`.
- **An operation `succeeded` but the state did not change:** look at `completed_but_unchanged`; treat the change as not made.

Guest commands, file operations, port changes, and SSH key changes all need an observed `running` machine.
