Vast.ai for VS Code
[!NOTE] This extension is unofficial and not affiliated with Vast.ai. It is a personal project of the author, who is a happy Vast.ai customer.
Manage Vast.ai GPU rentals without leaving the editor: search offers and create an instance, then connect to it with the full Remote-SSH experience — or manage instances you already have running, from a tree view in the Activity Bar.
This talks to the Vast.ai REST API directly (no dependency on the Python vastai CLI being installed). A local copy of the API's OpenAPI spec, pulled from docs.vast.ai, lives at docs/api-reference/openapi.yaml for reference.
Requirements
- A Vast.ai account and an API key from console.vast.ai/manage-keys.
- An SSH key registered to your Vast.ai account (same page) — connecting fails with
Permission denied (publickey) otherwise. If your default agent/keys aren't the registered one, point vastai.sshIdentityFile (see below) at the matching private key.
- Microsoft's Remote - SSH extension (
ms-vscode-remote.remote-ssh) for the full connect experience. Not strictly required — if it's missing, connecting falls back to an integrated terminal running plain ssh.
Getting started
- Run Vast.ai: Set API Key from the Command Palette (or click through the empty-state prompt in the Vast.ai Activity Bar view).
- Click + in the Vast.ai view to create an instance, or manage instances you already have from the tree.
Commands
| Command |
What it does |
Vast.ai: Set API Key |
Prompts for and stores your API key in VS Code's SecretStorage. |
Vast.ai: New Instance |
Multi-step wizard: GPU model → count → offer → image → disk size → create. Waits for the instance to come up and offers to connect. |
Vast.ai: Connect via SSH |
Writes/updates a Host vastai-<id> entry in ~/.ssh/config, waits for the SSH proxy to actually become reachable, then opens a new Remote-SSH window. |
Vast.ai: Start / Stop / Reboot / Recycle / Destroy Instance |
Lifecycle actions from the tree view's context menu. Reboot/Recycle/Destroy ask for confirmation first. |
Vast.ai: Refresh Instances |
Forces an immediate poll. |
Vast.ai: Open in Browser |
Opens the instance in the Vast.ai console. |
Vast.ai: Show Instance Details |
GPU, price, disk, image, SSH endpoint, location. |
Settings
| Setting |
Default |
Purpose |
vastai.pollIntervalSeconds |
20 |
Baseline polling interval for instance status. |
vastai.defaultImage |
vastai/base-image:@vastai-automatic-tag |
Suggested image in the New Instance wizard. |
vastai.defaultDiskGb |
20 |
Suggested disk size in the New Instance wizard. |
vastai.sshIdentityFile |
(none) |
Private key path to use when connecting, if your default agent/key isn't the one registered with Vast.ai. |
Architecture
src/
api/ Typed REST client (fetch-based) + hand-picked TS types for the slice of the API we use
auth/ API key storage (VS Code SecretStorage)
state/ VastApiStateDeputy — the single source of truth for instance state
views/ TreeView + status bar, both thin adapters over the deputy
commands/ Command implementations (create, start/stop/reboot/recycle/destroy, wait-for-running)
ssh/ ~/.ssh/config Host block upsert (via the `ssh-config` package, preserves existing content)
remote/ Remote-SSH handoff: reachability probe, window open, fallback to terminal
util/worry.ts Delayed/held-open progress notification helper
VastApiStateDeputy (src/state/vastApiStateDeputy.ts) owns all instance-state polling and is the single thing every UI surface and action flow reads from and listens to (onDidUpdate) — the tree view, the status bar, and the "wait until this instance is actually running/stopped/gone" logic in commands/waitForRunning.ts all watch the same feed instead of each polling the API independently. Callers who are about to care a lot for a little while (e.g. right after requesting a state change) call requestAttention() to get a burst of extra refreshes instead of waiting out the baseline interval.
This split exists because it wasn't always this way: an earlier version had the tree view doing double duty as both the UI adapter and the poller, and a separate independent 4-second poll loop for "wait until running" — two uncoordinated schedules that could (and did) disagree about what was currently true.
Development
npm install
npm run watch # esbuild watch build
Press F5 (Run → Start Debugging) to launch an Extension Development Host with the extension loaded. npm run compile type-checks without emitting; npm run package produces a .vsix via vsce.
[!NOTE] Right after pressing F5, you may see a warning "Errors exist after running preLaunchTask 'npm: watch'". This is an apparent VS Code quirk; hit "Debug Anyway" and the extension will load fine.
About the "Errors Exist" Warning

This warning seems related to whether VSCode believes there are "problems" with the codebase. My understanding: when IntelliSense checks for a few functions (setTimeout, fetch, similar), it doesn't find them and reports errors; those are the "Errors exist" that show up in the warning. The extension still loads and runs fine, so you can ignore it.
If you want it to go away, close the editor tabs for files with the squiggly red underline; this seems to be enough.
Early research indicates this is something to do with tasks.json, but light tinkering has not resolved the issue for me.
Scope
This is an MVP. Deliberately out of scope for now: volumes, serverless endpoints/workergroups, template management UI, billing/invoices, teams, webhooks — all present in the OpenAPI spec if picked up later.
There is currently no support for 2FA-protected tokens. This is a limitation of the extension; Vast.ai supports them quite well.
Also deliberately deferred: cross-window "you are here" awareness — detecting from inside a connected Remote-SSH window that it's a Vast.ai-managed host (via extensionKind: ["workspace", "ui"] + checking vscode.env.remoteName) and showing a distinct status bar item there with an explicit Stop & Disconnect action. Decided against building it for the initial version; revisit if the multi-window workflow turns out to matter in practice.