Send a Run to a GPU Box
Route a fine-tune to a remote GPU with –remote-host, a remote: YAML block, or the desktop 'Run on' dropdown
Add --remote-host gpubox to praisonai-train llm, put a remote: block in your config file, or pick “A remote server” in the desktop’s fine-tune form — the same instruction, three ways. The run happens on the other box; your laptop can close.
Quick Start
- Set up key auth once
The runner never accepts a password prompt — it uses your ssh agent and
~/.ssh/config.bash ssh-copy-id gpubox ssh gpubox echo ok # must print `ok` without a prompt - Send it with a flag
bash praisonai-train llm dataset.json \ --model unsloth/gemma-2-2b-it-bnb-4bit \ --remote-host gpuboxStreams the log while training. Ctrl-C stops the remote run — not just the tail.
- Or put it in the config file
yaml # config.yaml model_name: "unsloth/gemma-2-2b-it-bnb-4bit" dataset: - name: "yahma/alpaca-cleaned" remote: host: gpuboxbash praisonai-train llm --config config.yaml - Or pick 'A remote server' in the desktop
Open Fine-tune a model, set Run on → A remote server, and type the SSH alias. The remote python and remote directory fields reveal themselves with sensible defaults.
What Settles What — flag > YAML > default
The presence of a settled host is what routes the run remotely; there is no separate mode flag.
- No
host→ train here, exactly as before. - A flag overrides the file. An absent flag does not erase what the YAML says — only non-empty overrides win.
- A local run in the desktop posts no
remote:block, even if you typed a host and then switched back to “This computer”.
Options
Every key is expressible as a flag, in YAML, and in the desktop. All three drive the same code.
| Key | Flag | YAML | Type | Default | Description |
|---|---|---|---|---|---|
host | --remote-host | remote.host | str | (none) | SSH alias from ~/.ssh/config. Omit to train locally. |
python | --remote-python | remote.python | str | python3 | Interpreter on the remote host. |
workdir | --remote-workdir | remote.workdir | str | ~/.praisonai-train | Directory on the remote host to work in. |
gpus | --remote-gpus | remote.gpus | int | 1 | How many GPUs the run expects to find. |
What Is Shipped, and What Is Not
The dispatcher rewrites the config before sending it so the far side does the right thing.
- The remote: block is stripped
The remote host reads a config file too. Leaving
remote:in would make it find a host and dispatch again. The dispatcher removes it before shipping. - A local dataset is copied; a Hub dataset is not
The dispatcher walks the resolved
dataset— a string or a list-of-mappings — and copies the first local file it finds, whether it was named on the CLI or only insideconfig.yaml. A HuggingFace id or a path that lives only on the remote host is not a local file, so it is left alone. Name a local file explicitly withdata_fileswhen you wantnameto stay a label.You wrote Ships? dataset: "d.jsonl"(local file)Yes — copied to the run dir. dataset: [{name: "d.jsonl"}](local file)Yes — nameis the path, exactly as the trainer treats it.dataset: [{name: "my-set", data_files: "d.jsonl"}]Yes — data_filesis what ships;namestays the label.dataset: [{name: "org/alpaca-cleaned"}](Hub id)No — the far side loads it from the Hub. dataset: "/remote/only/path.jsonl"(only on the host)No — nothing to copy. A
dataset: "path.jsonl"string is normalised to[{name: "path.jsonl"}]in the shipped config for the remote trainer. - Credentials are refused up front
password,passphrase,token,key,secret,identity_file,private_keyin theremote:block are refused before any SSH — the file is shipped to the remote host and printed by--dry-run, so a credential in it would leak. Use your ssh agent and~/.ssh/config.
Preview Before You Spend the GPU-hours
--dry-run prints the resolved config, including the settled remote: block (credentials redacted), and sends nothing.
praisonai-train llm --config config.yaml --remote-host gpubox --dry-runThe remote: block appears in the preview with the host, interpreter, workdir, and GPU count the run would actually use:
dataset:
- name: yahma/alpaca-cleaned
model_name: unsloth/gemma-2-2b-it-bnb-4bit
remote:
gpus: 1
host: gpubox
python: python3
workdir: ~/.praisonai-trainA bad remote: block fails here too — --dry-run resolves and validates it, so a malformed block exits 1 with Bad remote settings before dispatch rather than after an hour of rented GPU.
Answers “what am I about to do, and where?” before an hour of rented GPU.
Ctrl-C Stops the Remote Run
Ctrl-C during the log tail is not “just close the local tab”. The dispatcher forwards SIGINT / SIGTERM to the remote runner, which stops the training process — the same as the desktop Stop button.
Common Refusals (all before any SSH)
Each of these exits 1 before a connection is opened. The forbidden value is never echoed back.
| You wrote | What happens |
|---|---|
remote: {host: h, password: hunter2} | Refused — must not carry a credential. This file is copied to the remote host and printed by --dry-run. Use an ssh agent instead. Value never echoed. |
remote: {host: "gpu; rm -rf /"} | Refused — not a plain SSH alias. Put connection details in ~/.ssh/config and name the alias here. |
remote: {host: h, workdir: "$(pwd)"} | Refused — the workdir is expanded by the remote shell; only letters, digits, ._-/~ allowed. |
remote: {host: h, python: "python; whoami"} | Refused — same character set as workdir. |
remote: gpubox (a string, not a mapping) | Refused — must be remote: {host: gpubox, ...}. |
remote: [] (an empty list) | Refused — remote: must be a mapping of key: value. An omitted key (or remote: null) means “train here”. |
remote: "" (an empty string) | Same refusal — a present-but-falsey value is a malformed block, not an absence. |
remote: false | Same refusal. |
remote: {host: h, gpus: 0} | Refused — gpus must be a positive integer. |
remote: {host: h, hostname: typo} | Refused — unknown key hostname. Known keys: host, python, workdir, gpus. |
Best Practices
Put connection details in ~/.ssh/config
Name only the alias in PraisonAI; keep host, user, and key in your ssh config:
Host gpubox
HostName gpu-01.example.com
User me
IdentityFile ~/.ssh/id_ed25519Then --remote-host gpubox, remote: {host: gpubox}, and the desktop’s alias field all mean the same thing.
Dry-run before you rent the GPU
--dry-run prints the resolved config with the remote: block and sends nothing. It catches a wrong host or wrong workdir before an hour of rented GPU.
Use this for 'kick it off and watch it'
This shortcut is one command — good for a single job you want to launch and tail. For manual preflight, detached start, reattach later, fetch the adapter, and stop, use the standalone praisonai-train remote sub-app.
Combine with multi-GPU on the remote box
praisonai-train llm --config config.yaml --remote-host gpubox --remote-gpus 4 sets the GPU expectation for the remote run. See Multi-GPU for the launcher side.