---
title: "GPU Training Nodes"
description: "Provision a raw GPU VM on demand, run a training or fine-tuning job, then tear it down — hands-off cloud-init pattern with self-destruct"
canonical: "https://docs.ainative.studio/docs/agent-cloud/gpu-training"
last-updated: "2026-10-18T18:41:28.290Z"
---

# GPU Training Nodes

Source: https://docs.ainative.studio/docs/agent-cloud/gpu-training

> Provision a raw GPU VM on demand, run a training or fine-tuning job, then tear it down — hands-off cloud-init pattern with self-destruct

## GPU Training Nodes

Provision a **raw GPU VM** (a DigitalOcean droplet), run a training or fine-tuning job on it,
then destroy it. Unlike [Dedicated GPU Deployments](/docs/agent-cloud/dedicated-deployments),
which **serve** a model steady-state, a training node is **burst compute** you spin up, train
on, and tear down.

**Base path:** `/api/v1/public/deployments/gpu-training`

---

## Inference vs Training — which do I want?

| | [Dedicated Deployment](/docs/agent-cloud/dedicated-deployments) | GPU Training Node |
|---|---|---|
| Purpose | **Serve** a model | **Train / fine-tune** a model |
| Mental model | Kept warm, latency-optimized | Spin up → train → tear down |
| You interact via | `chat/completions` proxy | SSH or cloud-init on a raw VM |
| Backing | DO dedicated inference | DO GPU droplet |
| Best for | Production / low-latency serving | Fine-tunes, experiments, batch jobs |

Rule of thumb: if you want to send requests to it, use a
[Dedicated Deployment](/docs/agent-cloud/dedicated-deployments). If you want to run
`python train.py` on it, use a GPU Training Node.

---

## Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| `GET` | `/deployments/gpu-training/sizes` | No | List GPU droplet sizes + live pricing |
| `POST` | `/deployments/gpu-training` | Yes | Provision a node |
| `GET` | `/deployments/gpu-training/{droplet_id}` | Yes | Get status + public IP |
| `DELETE` | `/deployments/gpu-training/{droplet_id}` | Yes | Destroy the node (stops billing) |

---

## List GPU sizes

No authentication required.

```bash
curl https://api.ainative.studio/api/v1/public/deployments/gpu-training/sizes
```

**Response (200):**
```json
{
  "sizes": [
    { "slug": "gpu-mi300x8-1536gb", "label": "AMD MI300X ×8 (1536GB VRAM)", "hourly_cost_usd": 15.92, "vram_gb": 1536 },
    { "slug": "gpu-h100x8-640gb",   "label": "NVIDIA H100 ×8 (640GB VRAM)",  "hourly_cost_usd": 23.92, "vram_gb": 640 },
    { "slug": "gpu-mi300x1-192gb",  "label": "AMD MI300X ×1 (192GB VRAM)",   "hourly_cost_usd": 1.99,  "vram_gb": 192 },
    { "slug": "gpu-h100x1-80gb",    "label": "NVIDIA H100 ×1 (80GB VRAM)",   "hourly_cost_usd": 3.39,  "vram_gb": 80 }
  ]
}
```

Prices come from DigitalOcean's live catalog at request time. The `gpu-mi300x8-1536gb` node
(1,536 GB VRAM) is large enough to fine-tune GLM-5-class models.

---

## Provision a node

```python
import requests

API_KEY = "sk_your_key"
BASE = "https://api.ainative.studio/api/v1/public"
H = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}

node = requests.post(
    f"{BASE}/deployments/gpu-training",
    headers=H,
    json={
        "name": "glm5-finetune",
        "size_slug": "gpu-mi300x8-1536gb",   # default
        "region": "atl1",                     # default
        "ssh_key_ids": [12345678],            # OR user_data — at least one required
    },
).json()

print(node["droplet_id"], node["hourly_cost_usd"])
```

**Request body:**

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | string | Yes | — | Node name |
| `size_slug` | string | No | `gpu-mi300x8-1536gb` | GPU droplet size |
| `region` | string | No | `atl1` | DigitalOcean region |
| `ssh_key_ids` | list[int] | No\* | — | DO SSH key IDs to inject (interactive access) |
| `user_data` | string | No\* | — | cloud-init script run on first boot (hands-off jobs) |

:::caution At least one access method is required
You must supply **`ssh_key_ids`, `user_data`, or both**. A node with neither would be
unreachable and would bill idle — the API rejects it with `400`.
:::

**Response (201):**
```json
{
  "droplet_id": 987654321,
  "name": "glm5-finetune",
  "status": "new",
  "size_slug": "gpu-mi300x8-1536gb",
  "hourly_cost_usd": 15.92
}
```

---

## Check status

```bash
curl https://api.ainative.studio/api/v1/public/deployments/gpu-training/987654321 \
  -H "Authorization: Bearer $TOKEN"
```

**Response (200):**
```json
{ "droplet_id": 987654321, "status": "active", "public_ip": "203.0.113.42" }
```

Poll until `status` is `active` and `public_ip` is populated before connecting.

---

## Destroy the node

```bash
curl -X DELETE https://api.ainative.studio/api/v1/public/deployments/gpu-training/987654321 \
  -H "Authorization: Bearer $TOKEN"
```

**Response (200):**
```json
{ "destroyed": true, "droplet_id": 987654321 }
```

Billing stops the moment the node is destroyed.

---

## Cost model

Billing runs from **boot** (`POST`) to **destroy** (`DELETE`) — the whole wall-clock window,
including boot, model download, and weight load, **not just** the training loop. On large
8×GPU nodes, cold-start is a real fraction of the bill.

**Batch experiments on one hot node** to amortize cold-start: pull weights once, then iterate,
rather than provisioning a fresh node per run.

---

## Hands-off pattern with cloud-init

Pass a cloud-init script as `user_data` to run a fully unattended job. A robust pattern is:
**train → merge → push the adapter → self-destruct**. The node destroys itself on completion
by calling the same `DELETE /gpu-training/{id}` endpoint with a short-lived AINative bearer
token, so it never bills idle after the job finishes.

```python
cloud_init = """#!/bin/bash
set -euo pipefail

# 1. train + merge + push the adapter to Hugging Face
python train.py && python merge_and_push.py

# 2. self-destruct — stop billing immediately on completion
curl -X DELETE \
  "https://api.ainative.studio/api/v1/public/deployments/gpu-training/${DROPLET_ID}" \
  -H "Authorization: Bearer ${AINATIVE_TOKEN}"
"""

node = requests.post(
    f"{BASE}/deployments/gpu-training",
    headers=H,
    json={"name": "unattended-finetune", "user_data": cloud_init},
).json()
```

:::tip Self-destruct guarantee
When the job finishes (success or failure), the node calls
`DELETE /deployments/gpu-training/{droplet_id}` on itself. Combined with the
`ainative-gpu-training` tag applied to every node on DigitalOcean, this makes idle spend
easy to prevent and orphaned nodes easy to find and clean up.
:::

Only a scoped AINative token needs to reach the node — you never place a raw DigitalOcean API
key on the VM.

---

## Next steps

- [Dedicated GPU Deployments](/docs/agent-cloud/dedicated-deployments) — serve a model on reserved GPU capacity
- [Inference Overview](/docs/agent-cloud/inference) — chat completions, embeddings, rerank, audio
- [Authentication](/docs/getting-started/authentication) — API keys and bearer tokens
