# Install Endpoint

This page covers the request fields, defaults, and response of `POST /api/vm/install`.

## Request

```bash
curl -N -X POST http://192.168.0.11:8080/api/vm/install \
  -H "Content-Type: application/json" \
  -d '{
    "name": "web",
    "os": "ubuntu",
    "version": "24.04",
    "cpu": 2,
    "ram": 4096,
    "disk": "32G",
    "node": "pve2",
    "pubkey": "ssh-ed25519 AAAA... user@laptop"
  }'
```

## Fields

| Field | Type | Required | Default | Rule |
|---|---|---|---|---|
| `os` | string | Yes | — | `debian` / `ubuntu` / `rockylinux` |
| `version` | string | Yes | — | See [supported versions](/os-init-scripts#supported-versions-and-images) |
| `id` | int | No | auto | Used as is without an availability check; also sets the last IP octet |
| `name` | string | No | VMID | Final name becomes `<name>-<vmid>` |
| `node` | string | No | main node | Migrate to this node after creation, before first boot |
| `cpu` | int | No | `1` | ≤ 0 → 1; capped by `VM_MAX_CPU` |
| `ram` | int | No | `512` | MB; < 512 → 512; capped by `VM_MAX_RAM` |
| `disk` | string | No | `16G` | Parses the number after stripping `G`; < 16 → `16G`; ≥ `VM_MAX_DISK` → the cap |
| `user` | string | No | per OS | Cloud-init user; defaults to `debian` / `ubuntu` / `rocky` |
| `passwd` | string | No | `passwd` | Cloud-init user password |
| `pubkey` | string | No | — | Extra SSH public key to inject |

The server overwrites `ip`, `gateway`, and `storage` from the VMID, `GATEWAY`, and `ASSIGN_STORAGE`; values sent by clients have no effect.

The SSH readiness check always logs in as the OS default user (`debian` / `ubuntu` / `rocky`), while the init script logs in as `user`; with a custom `user`, both accounts must accept the main node's key.

## Response

| Case | Response |
|---|---|
| JSON parsing or `os` / `version` validation fails | `400` JSON: `{"success": false, "message": "please check your input:..."}` |
| Client IP not in `ALLOW_IPS` | `200` with a single `data:` error line |
| Otherwise | An SSE stream reporting each [Provisioning Pipeline](/provisioning-pipeline) stage, ending with `event: close` |

The last four events on success:

```text
data: {"step":"VM initialization > finalizing","status":"success","message":"[+] VM installation completed in 182.41s"}

data: {"step":"VM initialization > finalizing","status":"success","message":"[*] VMID: 120"}

data: {"step":"VM initialization > finalizing","status":"success","message":"[*] IP: 192.168.0.120"}

data: {"step":"VM initialization > finalizing","status":"success","message":"[*] User: ubuntu"}
```

SSE streams always return HTTP 200; success means no event with `status: "error"` appeared.
