# Setup scripts

> Run a shell script once, right after a new machine first starts, and check whether it worked.

A setup script runs once, as `/bin/sh -c <script>`, after a new machine is first ready. Use it to install packages, clone a repository, or write configuration.

## Add one when you create a machine

```sh tab="CLI"
vy new --name dev --setup-script ./setup.sh --wait
cat setup.sh | vy new --name dev --setup-script - --wait
```

```ts tab="TypeScript"
const machine = await vy.machines.create({
  name: "dev",
  setupScript: "apt-get update && apt-get install -y jq",
});
```

```python tab="Python"
machine = vy.machines.create(
    name="dev",
    setup_script="apt-get update && apt-get install -y jq",
)
```

```sh tab="curl"
# In the create body:
#   "setup_script": "apt-get update && apt-get install -y jq"
```

The script can be at most 65,527 bytes (65,424 on a machine with an [environment](/docs/guides/environments)) and can't contain NUL. It can only be set at creation. On a machine with an environment, the script runs after the environment is installed, with its variables loaded.

## Check the result

The setup script is its own exec [operation](/docs/concepts/operations), queued with the create and run right after it. The machine's `setup_operation_id` names it.

```ts tab="TypeScript"
import { OperationError } from "@vmyard/sdk";

try {
  await vy.operations.wait(machine.data.setup_operation_id!);
  console.log("setup finished");
} catch (error) {
  if (!(error instanceof OperationError)) throw error;
  console.log("setup failed:", error.errorCode, error.operation.exec_result);
}
```

```python tab="Python"
from vmyard import OperationError

try:
    vy.operations.wait(machine.data["setup_operation_id"])
    print("setup finished")
except OperationError as error:
    print("setup failed:", error.error_code, error.operation.get("exec_result"))
```

```sh tab="curl"
SETUP_ID=$(curl -s "$VMYARD_API_URL/v1/machines/$MACHINE_ID" \
  -H "Authorization: Bearer $VMYARD_API_KEY" | jq -r .setup_operation_id)
curl "$VMYARD_API_URL/v1/operations/$SETUP_ID" -H "Authorization: Bearer $VMYARD_API_KEY"
```

| Outcome | What it means |
| --- | --- |
| `succeeded`, `exit_code` 0 | The script ran and exited cleanly. |
| `failed` with `exec_nonzero` | The script exited non-zero. The machine keeps running; read the output in `exec_result`. |
| `failed` with `exec_timed_out` | The script ran longer than 10 minutes. The machine keeps running. |
| `failed` with `setup_prerequisite_failed` | The create, or the environment install before it, didn't succeed, so the script never ran. |

The `vy new --wait` and SDK `create` calls wait for the create operation, not the setup script. Wait for `setup_operation_id` before you rely on what the script installs.

## Limits

The script runs with the longest timeout a command can have (10 minutes). Output is captured up to 1 MiB, like any command. Reusing a create's idempotency key with a different script is `409 idempotency_conflict`.
