> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basaltic.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# The instance lifecycle

> What start, stop, reboot, resize and reinstall each preserve, and what they destroy.

An instance carries two states and they answer different questions.

<Columns cols={2}>
  <Card title="desired_state" icon="target">
    What you **asked for**. Three values, because there are three things you
    can ask an instance to be.
  </Card>

  <Card title="current_state" icon="activity">
    Where it **actually is**. Read this one to answer "is it up".
  </Card>
</Columns>

They are not two halves of one answer — they are a request and its progress.
`POST /v1/instances/{instance_id}/start` sets `desired_state` to `running`
immediately, and `current_state` stays `stopped` until the guest is actually
up. That pairing is the honest reading of "asked to start, not there yet".

```mermaid theme={null}
stateDiagram-v2
    [*] --> pending: create
    pending --> building: provisioning starts
    building --> running: guest boots
    running --> stopping: stop
    stopping --> stopped: guest is down
    stopped --> running: start
    running --> rebooting: reboot
    rebooting --> running: guest is back
    stopped --> stopped: resize / reinstall
    building --> error: provisioning failed
    error --> running: start
    running --> deleting: delete
    stopped --> deleting: delete
    deleting --> deleted: teardown complete
```

### `desired_state`

Three values, and every call that changes one sets it to one of them.

| `desired_state` | Set by                |
| --------------- | --------------------- |
| `running`       | Create, Start, Reboot |
| `stopped`       | Stop                  |
| `deleted`       | Delete                |

Nothing else appears here. `stopping` is not something anyone requests — it is
where the instance has got to — which is why it lives on the other field.

### `current_state`

| `current_state` | Meaning                                                                                                        |
| --------------- | -------------------------------------------------------------------------------------------------------------- |
| `pending`       | The row exists; provisioning has not begun.                                                                    |
| `building`      | The boot disk, interfaces and seed are being built.                                                            |
| `running`       | The guest is up.                                                                                               |
| `stopping`      | A graceful shutdown is in flight.                                                                              |
| `stopped`       | Down, and kept — this is the state resize and reinstall need.                                                  |
| `rebooting`     | A reboot is in flight.                                                                                         |
| `migrating`     | The platform is moving the guest between hosts. Transient, nothing you asked for, and it returns to `running`. |
| `deleting`      | Teardown started.                                                                                              |
| `deleted`       | Teardown finished. The row is removed shortly after, so reads start answering `404`.                           |
| `error`         | A build or lifecycle step failed. Read every entry in `faults`.                                                |

<Note>
  Treat an unrecognized `current_state` as "busy, do not act" rather than as a
  failure. The list grows as the platform learns to tell states apart — a
  guest that has crashed currently reports `stopped`, because the host cannot
  yet distinguish a panic from a clean shutdown.
</Note>

## Start, stop, reboot

Each is a `202` with an empty body — poll the instance to see the result.

<Tabs>
  <Tab title="Console">
    Open the instance from **Compute → Instances**. The header offers only the
    actions its current state allows: **Start** while it is stopped, **Stop**
    and **Reboot** while it is running.

    A hard power cycle is not among them. It sits on the **Settings** tab as
    **Hard reboot**, described there as *Power-cycles the instance without a
    clean OS shutdown* and gated behind a typed confirmation.
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/instances/{instance_id}/start
    POST /v1/instances/{instance_id}/stop
    POST /v1/instances/{instance_id}/reboot
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic compute instance start <instance-id>
    basaltic compute instance stop <instance-id>
    basaltic compute instance reboot <instance-id>
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    c := compute.New(cfg)
    inst, err := c.StartInstance(ctx, instanceID)
    inst, err = c.StopInstance(ctx, instanceID)
    inst, err = c.RebootInstance(ctx, instanceID, &compute.InstanceRebootRequest{})
    ```
  </Tab>
</Tabs>

<AccordionGroup>
  <Accordion title="Start" icon="play">
    `POST /v1/instances/{instance_id}/start` accepts an instance in `stopped`
    **or in `error`** — the second case is how you retry an instance that
    failed on the way up, without recreating it. Any other state is a `409`.
  </Accordion>

  <Accordion title="Stop" icon="square">
    `POST /v1/instances/{instance_id}/stop` requires `running`. Anything else
    is a `409` — including an instance already `stopped`, so this is not a
    "make it stopped" idempotent call.
  </Accordion>

  <Accordion title="Reboot" icon="rotate-cw">
    `POST /v1/instances/{instance_id}/reboot` requires `running`. The default
    is a graceful ACPI reboot the guest can act on; `{"hard": true}` is a power
    cycle — the reset button, with no chance to flush anything.
  </Accordion>
</AccordionGroup>

## Resize

A resize changes **vCPU and RAM, and nothing else.** Disks are untouched,
addresses are untouched, the guest's data is untouched.

<Tabs>
  <Tab title="Console">
    **Resize** on the instance header opens **Resize instance**, a flavor
    picker that summarizes **Current** against **New flavor** before you commit
    and then asks you to confirm.

    <Warning>
      The console offers **Resize** while the instance is still running, but
      the resize itself needs it stopped — submitting from a running instance
      comes back as **Failed to resize instance**. Stop it first.
    </Warning>
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/instances/{instance_id}/resize
    { "flavor": "550e8400-e29b-41d4-a716-446655440000" }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic compute instance resize <instance-id> \
      --flavor 550e8400-e29b-41d4-a716-446655440000
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    err := compute.New(cfg).ResizeInstance(ctx, instanceID,
        &compute.ResizeInstanceRequest{
            Flavor: "550e8400-e29b-41d4-a716-446655440000",
        })
    ```
  </Tab>
</Tabs>

Two things about it are worth knowing before you plan a resize window:

<Steps>
  <Step title="The instance must be stopped">
    A running guest's CPU and memory maximums cannot be changed underneath it,
    so a resize on a running instance is a `409`. Stop it, resize, start it.
  </Step>

  <Step title="The new size is applied at the next start">
    The call records the target and returns. The guest comes up on the new
    flavor when you start it — there is no separate confirm step, and no
    state in which the instance is half-resized.
  </Step>
</Steps>

<Warning>
  **A resize does not move the instance to another host.** Growing means the
  extra vCPU and RAM have to be free on the host it is already on, so a resize
  can be refused for capacity while the region as a whole has plenty. Moving to
  a `dedicated` flavor is the strictest case: it needs whole threads nothing
  else may run on, which a busy-but-not-full host may not have.
</Warning>

Two more refusals, both `400`: resizing to the flavor the instance already
uses, and resizing onto a `loadbalancer` or `database` flavor.

An instance's network ceiling moves with the flavor as part of the resize, so
a downsize gives up the larger flavor's bandwidth too.

## Reinstall

`POST /v1/instances/{instance_id}/reinstall` re-images the boot disk while
keeping the instance itself. Also stopped-only, and also applied at the next
start.

<Columns cols={2}>
  <Card title="Kept" icon="check">
    The instance id, name, CRN, IP addresses and MAC addresses, its network
    interfaces, its keypairs, its cloud-init seed, and **every attached data
    volume**.
  </Card>

  <Card title="Replaced" icon="triangle-alert">
    The boot volume. A fresh one is cloned from the image and swapped in, and
    **the old one is deleted.** Everything on the root filesystem is gone.
  </Card>
</Columns>

<Tabs>
  <Tab title="Console">
    The console calls this **Replace root volume**, and it is on the
    instance's **Settings** tab, in the danger zone. It opens an **Image** and
    a **Boot volume** card — *Operating system for the replacement root volume.
    The current one is deleted* — and confirms with the instance name typed
    back.

    <Note>
      Same operation, different name. Nothing in the console is labelled
      "reinstall": look for **Replace root volume**, which is the more literal
      description of what happens to the disk.
    </Note>
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/instances/{instance_id}/reinstall
    { "image": "debian-13:20260807", "size_gb": 40, "volume_type": "nvme" }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic compute instance reinstall <instance-id> \
      --image debian-13:20260807 --size-gb 40 --volume-type nvme
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    err := compute.New(cfg).ReinstallInstance(ctx, instanceID,
        &compute.ReinstallInstanceRequest{
            Image: basaltic.String("debian-13:20260807"),
            SizeGB: basaltic.Int(40), VolumeType: basaltic.String("nvme"),
        })
    ```
  </Tab>
</Tabs>

Every field is optional. Omit `image` and it reinstalls from the image the
instance already has; omit `size_gb` and the replacement is the image's
`min_disk_gb`; omit `volume_type` and it lands on the region default. The same
floor applies as at launch — `size_gb` must be at least the image's
`min_disk_gb`, and within 1–16384 GB.

Because the addresses survive, reinstall is the operation for "same machine,
clean OS" — a rebuild where anything pointing at the instance keeps working.

## Delete

<Tabs>
  <Tab title="Console">
    **Delete instance** is on the instance's **Settings** tab, in the danger
    zone. The confirmation restates the rule below — *Volumes marked delete on
    termination are destroyed with it; others are detached and kept* — and
    needs the instance name typed back.
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    DELETE /v1/instances/{instance_id}
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic compute instance delete <instance-id>
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    err := compute.New(cfg).DeleteInstance(ctx, instanceID)
    ```
  </Tab>
</Tabs>

It answers `202`. The instance moves to `deleting`, the guest is torn down, and
the row is removed once its interfaces and volumes have been reclaimed — so a completed delete stops answering reads
entirely rather than leaving a `deleted` row behind.

Deleting an instance already `deleting` is accepted and re-drives the teardown
rather than starting a second one.

### What survives a delete

|                                                        |                                                            |
| ------------------------------------------------------ | ---------------------------------------------------------- |
| Boot volume                                            | **Destroyed.** Created with `delete_on_termination: true`. |
| Launch-time `volumes`                                  | Destroyed, unless you sent `delete_on_termination: false`. |
| A volume you attached later                            | **Released back to `available`,** not destroyed.           |
| A floating IP from `networks[].floating_ip_assignment` | Released — the launch allocated it.                        |
| A floating IP you allocated and attached               | **Yours.** It is detached and stays allocated.             |
| Keypairs, images                                       | Untouched. Neither is instance-scoped.                     |

<Tip>
  To keep a boot disk past its instance, flip the flag on the attachment:
  `PATCH /v1/instances/{instance_id}/volumes/{volume_id}` with
  `{"delete_on_termination": false}`. The volume is then unbound and returned
  to `available` on teardown instead of destroyed. This works on the boot
  volume like any other attachment.

  In the console it is the **Delete on termination** switch on the instance's
  **Volumes** tab, one per attachment.
</Tip>
