Opinionated PowerShell launcher for a FLUX.2 + Wan 2.2 + SUPIR studio stack on Vast.ai. First-run wizard, DPAPI-secured HF token, parallel downloads, polled detached setup.
One command takes a freshly rented RTX 5090 instance from blank Ubuntu to a fully provisioned ComfyUI studio with eleven model files (~101 GB), seven custom node packs, a clean LUT pipeline, and an SSH tunnel open in your browser — in about 25 to 40 minutes depending on bandwidth. The script is heavily commented; the rationale behind every non-obvious choice (why two download waves rather than eleven simultaneous, why -O on every scp call, why the pip step prints visibly instead of quietly) lives in the file itself.
This is v1.0. It is currently Windows-only (PowerShell 5.1+) and Vast.ai-only. Cross-platform and multi-provider are on the roadmap below.
Image generation
- FLUX.2 Dev (fp8mixed) with Mistral-Small-3.2-24B encoder and the 128-channel FLUX.2 VAE
- FLUX.2 Klein 9B (BF16) with Qwen-3 8B encoder
Motion (image-to-video)
- Wan 2.2 14B I2V — both high-noise and low-noise variants for the two-stage sampler
- umT5-XXL text encoder, Wan 2.1 VAE (Wan 2.2 reuses Wan 2.1's VAE unchanged)
Upscaling
- SUPIR-v0F fp16 (Kijai's pruned fidelity variant) on SDXL 1.0 base
Custom nodes
- ComfyUI-Manager, ComfyUI-GGUF, comfyui_controlnet_aux, rgthree-comfy
- ComfyUI-Flux2Klein-Enhancer (identity-transfer nodes for Klein workflows)
- ComfyUI-SUPIR (Kijai's maintained SUPIR fork)
- WAS Node Suite (for the
Image Apply LUTnode, symlinked to a clean LUT directory)
Total download: roughly 101 GB across two parallel waves. A Vast.ai instance with at least 130 GB of disk in /root is required; the script hard-fails before downloading anything if disk is short.
- Windows 10 or 11 with PowerShell 5.1 or later (PowerShell 7 works too)
- A Vast.ai account with credit and SSH key uploaded
- An SSH key pair on your machine — typically
%USERPROFILE%\.ssh\id_ed25519and.pub - A Hugging Face account with an access token that has agreed to the FLUX.2 license (Klein needs license acceptance; Dev does not, but the same token works for both)
- A rented Vast.ai instance with at least 130 GB of disk in
/rootand an RTX 5090 (the FLUX.2 fp8 weights are natively suited to Ada/Hopper/Blackwell; older GPUs work but with quality/speed tradeoffs)
Clone or download this repo to a folder on your Windows machine.
PowerShell on a fresh Windows install often refuses to run unsigned local scripts. If .\comfy-connect.ps1 fails with script is not digitally signed or cannot be loaded, run these two commands once:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
Unblock-File .\comfy-connect.ps1The first allows you to run unsigned scripts you wrote or downloaded yourself. The second removes Windows' "downloaded from internet" Mark-of-the-Web tag from this specific file. Both affect only your user account and are standard PowerShell hygiene — they don't weaken system security.
.\comfy-connect.ps1First run. A wizard prompts you for four things, saves them to %APPDATA%\comfyui-vast-bootstrap\config.json, and encrypts your Hugging Face token using Windows DPAPI into a separate hf_token.secure file next to the config. The wizard takes about 90 seconds.
You'll be asked for:
- SSH key path — defaults to
%USERPROFILE%\.ssh\id_ed25519. Warns if the file doesn't exist but doesn't fail (you might create it after running once). - Local port for the ComfyUI tunnel — defaults to 8188. Must be 1024–65535.
- Local directory for downloaded outputs — defaults to
%USERPROFILE%\Pictures\ComfyUI-outputs. The script offers to create it if it doesn't exist. - Hugging Face token — pasted with hidden input via
Read-Host -AsSecureString. Must start withhf_. Encrypted with DPAPI before being written to disk; the plaintext never touches the filesystem.
Then it asks for the Vast.ai SSH string from your instance console — paste the line that looks like ssh -p 12345 [email protected] -L .... This one is prompted on every run because it changes per instance.
Subsequent runs. The wizard is skipped entirely. You see one line confirming the token loaded from the secure store, then go straight to the Vast SSH prompt.
Total user interaction after first-time setup: one prompt per run.
-Reconfigure— Ignores the saved config and re-prompts for every field. Defaults shown in brackets are your previously saved values, so pressing Enter through fields you don't want to change keeps them intact. The HF token re-prompts fresh (secrets aren't echoed).-ConfigPath <path>— Uses a custom config file location instead of the default%APPDATA%path. Useful for keeping multiple profiles (personal vs. client, dev vs. prod).
The script generates a single bash payload, scp's it to /tmp/, launches it under nohup so SSH disconnects don't kill it, and polls every 10 seconds for a status marker the bash payload writes to /root/setup.log. The poll loop is robust to brief SSH outages — if five consecutive polls fail (about a minute of unreachability), it pauses and asks whether to keep waiting or abort. Heavy load during the pip install or Wan 2.2 download can cause exactly this kind of transient unreachability; the script is designed to ride through it.
The bash payload does eight stages, in order:
- System check — verifies GPU and that
/roothas at least 130 GB free - ComfyUI clone — depth-1 clone with three retry attempts
- Core pip install — visibly progress-printed (not
-q; see engineering note below) - Custom nodes — clones and installs seven node packs, each guarded by an idempotency check so re-runs are safe
- Model directories — creates the standard ComfyUI tree plus
models/luts/anduser/default/workflows/ - Model downloads — eleven files across two parallel waves, with SHA-256 verification hooks for files you choose to pin
- LUT pipeline configuration — symlinks
models/luts/into the WAS Node Suite's expected location so any LUT you upload to the standard path is also seen byImage Apply LUT - ComfyUI launch — bound to
127.0.0.1only (the SSH tunnel is the sole ingress), PID file saved for clean restarts
Then on the Windows side, the script opens an SSH tunnel in a new PowerShell window, polls /object_info until ComfyUI's nodes are registered (not just / returning 200 — that's a misleading readiness signal that fires several minutes before the model is actually loaded), and opens your default browser to http://localhost:8188 when it's truly ready.
The script does not upload workflows. You have three options for getting your workflow JSONs onto the running instance:
- Drag and drop. Open the ComfyUI canvas in your browser and drop a
.jsonfile directly onto it. Loads instantly. Best for ad-hoc work. - Sidebar dropdown. scp the JSON into
/root/ComfyUI/user/default/workflows/and it appears in the Load menu's dropdown on the next ComfyUI refresh. - ComfyUI-Manager. Use the Manager's workflow import UI.
The script prints the exact scp command you need after launch, with your SSH key path and instance details already filled in.
Same pattern — manual upload, not automated:
scp -O -i "$env:USERPROFILE\.ssh\id_ed25519" -P <port> <path-to-your-luts>\*.cube root@<ip>:/root/ComfyUI/models/luts/The exact command is printed at the end of every run with your specific values filled in. Because the script symlinks models/luts/ into the WAS Node Suite's expected location, uploads to that single path are immediately visible to the Image Apply LUT node.
The exact scp command is printed at the end of every run, with your SSH key, port, IP, and outputs directory pre-filled. Typically:
scp -O -i "$env:USERPROFILE\.ssh\id_ed25519" -P <port> -r root@<ip>:/root/ComfyUI/output/ "<your-outputs-dir>"The script encodes a number of decisions that were arrived at by failure rather than by reading docs. Each is commented in-line; the highlights:
Two download waves, not eleven simultaneous. Hugging Face starts soft-rate-limiting around 8+ concurrent large downloads from one client. aria2c -x 16 -s 16 already opens 16 connections per file; multiplying that by eleven trips the limiter and stalls. Two waves keep total time low (about +8 minutes vs. one mega-wave) without throttle risk.
pip install runs visibly, not -q. The -q flag combined with SSH proxy idle-drops made the pip step appear to hang indefinitely during long dependency resolution (especially for the WAS Node Suite, which pulls in OpenCV, numba, scipy, transformers extras). Visible progress output keeps the SSH connection active and gives the polling loop something to display. --prefer-binary is also passed to avoid slow C-extension compilations, and --no-cache-dir prevents stale-cache issues on ephemeral cloud instances.
Detached nohup with polling, not interactive SSH. A 25-to-40-minute provisioning run is too long to leave attached to an interactive SSH session — any network blip on the Windows side kills it. The script launches the bash payload detached, then polls /root/setup.log for status markers. SSH can drop and reconnect arbitrarily without affecting the remote work.
-O on every scp call. Some Vast.ai instances run an OpenSSH server whose SFTP subsystem mismatches newer scp clients, producing Unimplemented sftp packet errors mid-upload. -O forces the legacy SCP protocol and sidesteps the issue. Applied to every scp call in the script — the upload, the verbose retry, the help-text examples.
SSH ConnectTimeout=10 globally, ServerAliveInterval=15 only on polling. A TCP-open-but-unresponsive sshd (typical during heavy memory pressure or pip install) blocks indefinitely without ConnectTimeout. But ServerAliveInterval on scp risks spurious mid-transfer disconnects on slow links, so it's only applied to the polling-loop SSH calls where a hung connection genuinely needs to time out fast.
DPAPI for HF token storage, with .Trim() on read. Windows Data Protection API is the built-in zero-dependency way to encrypt secrets per-user-per-machine. The token is never written to disk in plaintext. However: Set-Content -Encoding UTF8 appends a CRLF to the encrypted hex blob, and ConvertTo-SecureString rejects any leading/trailing whitespace with Input string was not in a correct format. The read side trims explicitly; the write side stays unchanged so existing encrypted files keep working.
Workflow upload removed (vs. the original script). Earlier versions of this script tried to auto-upload workflow JSONs from the script directory. That coupling — your local script directory contents driving remote state — created edge cases (what counts as a workflow? what if you have a package.json next to the script?) without much benefit. Uploading workflows is a five-second drag-and-drop in the ComfyUI UI. The script now does provisioning only.
script is not digitally signed on first run. See "First-time setup" above. Run Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned and Unblock-File .\comfy-connect.ps1 once.
Could not decrypt HF token on a subsequent run. DPAPI-encrypted secrets are tied to a specific Windows user account on a specific machine. If you copied your config between machines, switched Windows users, or reinstalled Windows, the secure file becomes unreadable. The script handles this automatically: it prints an explanation and re-prompts for the token. Just re-paste it; the rest of your config (paths, port) survives.
Permission denied (publickey) from scp. Your SSH key isn't at the path stored in your config, or isn't authorized on the Vast.ai account. Verify with .\comfy-connect.ps1 -Reconfigure and check the SSH key path is correct. If your key is in a non-standard location, pass it during the reconfigure prompt or edit %APPDATA%\comfyui-vast-bootstrap\config.json directly.
Setup polls show no new log output for several minutes. Normal during the pip install step (the WAS Node Suite pull takes 3 to 5 minutes) and during the largest model downloads (Klein 9B is 18 GB). The script will prompt you to abort only after a full minute of consecutive SSH poll failures; under normal pip-install conditions SSH stays reachable and only the log output is slow. Wait.
Wave 1 had failures; not starting Wave 2. Usually one of: (a) your HF token doesn't have the FLUX.2 Klein license accepted — visit the Klein model page and click Accept, (b) HF rate-limited you, in which case wait a few minutes and re-run (downloads resume from where they stopped via aria2c -c), or (c) your token is malformed — -Reconfigure to re-enter.
ComfyUI launched but the browser opens to a UI that can't run jobs. Wait. The Windows-side polling waits up to 8 minutes for /object_info to return a populated JSON, which is the right readiness signal. If the browser opens before nodes are registered, the script prints "Timed out" and gives you the log path to check. Usually 30 more seconds of patience fixes it.
This is v1.0. Planned next:
-
v0.2 — Profile selection. Add
--profile minimal | character | motion | studioso users who don't need the full 101 GB stack can pick a lighter download. Minimal would be FLUX.2 Dev only (~17 GB); character would be Dev plus Klein and the identity-transfer nodes (~57 GB); motion would add Wan 2.2 (~85 GB); studio is what v1.0 ships today (~101 GB). -
v0.3 — Cross-platform Python rewrite. Rewrite the Windows-side launcher in Python so it runs on macOS and Linux too. The remote bash payload stays as-is. Keychain on macOS and
keyring+ libsecret on Linux replace DPAPI for secure token storage. -
v0.4 — Multi-provider abstraction. Same CLI, same wizard, but with adapters for Runpod and Modal in addition to Vast.ai. The user picks a provider once during configuration, the script handles the rest.
Open issues welcome, especially around (a) provider behaviors I haven't seen, (b) workflows that would benefit from a --profile you can't approximate with the current four, and (c) anything in the engineering choices section that turns out to be wrong on your hardware.
MIT. Free to copy, adapt, redistribute.
Author — Mohammed Jalil Bachar · Neuchâtel, Switzerland Related work — flux2-prompt-guide (architecture-grounded prompt engineering reference for FLUX.2)