fcloud
Guides

CLI Reference

Every fcloud command, grouped by task.

Installation & authentication

curl -fsSL https://fcloud-home.vercel.app/install.sh | sh   # install + sign in + agent skills

Or, with your own Python:

pip install fcloud-sdk
fcloud login            # sign in via the browser; saves a key + installs agent skills
fcloud set_token <key>  # already have a key (CI, agents)
fcloud setup            # re-run the skill install / save a key you already have
fcloud health           # verify connectivity

How the pieces fit

ConceptWhat it is
sessionA persistent /workspace filesystem, not a held GPU. Cold and $0 until used; exec/run/shell/upload bring it online with its files intact, and fcloud stop halts spend but keeps the files. Address an existing one with --on <SID> (or a bare s-... argument).
volumeA named folder you mount into a session with --volume — how data moves between sessions. Writes commit back as a new volume version when the session detaches or closes. See Volumes.
processWork running inside a session. exec/run wait in the foreground; spawn returns a process id you then wait/logs/kill.
jobA run-to-completion session with no saved filesystem. Its only durable outputs are its volumes and its logs. See Batch.
sweepfcloud map fans one command out over many argument bindings as a durable batch you inspect with fcloud sweep. See Batch.

Global flags

FlagDescription
--jsonMachine-readable JSON output on any command. Status messages go to stderr.

Place --json before your remote command: flags after the command — or after a -- — belong to your program. So fcloud exec --json -- python app.py selects fcloud's JSON output, while fcloud exec -- python app.py --json passes --json to app.py. Unknown flags are rejected (a typo'd --limitt is an error, never silently ignored).

Find hardware

fcloud skus     # list available GPU/CPU SKUs
fcloud health   # check dispatcher connectivity

SKUs follow the pattern gpu_<count>x_<model> (e.g. gpu_1x_l4, gpu_8x_h100) or cpu_<...>. If a SKU has no free capacity, the session queues until a host is provisioned. Browse the catalog.

Run code

CommandDescription
fcloud exec [--sku SKU] [--on SID|SID] [--volume NAME[:MOUNT]] [--wait 30s] <cmd...>Run a command on a GPU host
fcloud run <file|dir> [--sku SKU] [--on SID|SID] [--script NAME] [-- args...]Upload and run a script
fcloud shell [--sku SKU] [--on SID|SID] [--volume NAME[:MOUNT]]Interactive shell (fcloud-native PTY)
fcloud ssh <SID>SSH into a session (local OpenSSH client)
fcloud tunnel <SID> [--port PORT]SSH ProxyCommand / port tunnel

Without --on, exec and run create a throwaway session, do the work, and release it automatically — its filesystem is still saved and downloadable afterwards. With --on <session-id> (or a positional s-... argument) they attach to an existing session, run, and detach, leaving the session alive.

fcloud exec --sku gpu_1x_l4 nvidia-smi -L        # one-off command
fcloud run train.py --sku gpu_1x_l4              # upload + run one file
fcloud run . --script train.py -- --epochs 50    # upload a dir, pass args
fcloud exec --on s-abc123 python train.py        # attach to a session
fcloud exec s-abc123 -- python train.py          # positional alias for --on

Working directory

Commands run from /workspace by default — the same place fcloud upload puts your files. An uploaded train.py runs as python train.py; relative writes land in /workspace and persist. To run elsewhere for one command, cd inside it: sh -c 'cd /some/dir && ...'. This applies uniformly to exec, run, spawn, and shell.

Waiting, detaching, and long commands

exec waits in the foreground for a budget (default 30s; set with --wait 10m or the alias --timeout). If the command finishes in time, stdout/stderr are printed and the CLI exits with the remote return code. If not, exec does not kill it — it detaches and prints a process id:

Command is still running; use fcloud logs s-abc123 proc-456 --follow

In --json mode a detached command returns status: "running" and returncode: null plus session_id and process_id — treat that as success-with-handle, then fcloud wait <sid> <pid> or fcloud logs <sid> <pid> --follow. For jobs you expect to run long, prefer spawn + wait from the start (see below).

Output is bounded by default so a noisy command can't flood the caller. If the JSON response has stdout_truncated: true, don't rerun the command — fetch the durable log instead: fcloud logs <sid> <pid> --output all.

Shell parsing

Pass commands as normal argv when you can. A single quoted string runs as shell text (via bash -lc), and sh -c works for explicit shell syntax:

fcloud exec --on s-abc123 python train.py --epochs 10
fcloud exec --on s-abc123 'cd /workspace; ls'
fcloud exec --on s-abc123 -- sh -c 'sleep 45; echo done'

Unquoted metacharacters are handled by your local shell first: fcloud exec --on s-abc123 sleep 45; echo done runs sleep 45 remotely, then echo done locally.

Background processes

CommandDescription
fcloud spawn [--on SID|SID] [--volume NAME[:MOUNT]] [--emit-pid FILE] <cmd...>Start a background process; prints a process id
fcloud wait <SID> <PID> [--timeout DUR]Block until the process exits; exits with its exit code
fcloud logs <SID> [PID] [--follow] [--output MODE] [--stream stdout|stderr|combined]Durable logs for a session or process
fcloud kill <SID> <PID>Kill a background process

The reliable pattern for long jobs:

fcloud spawn --on s-abc123 python train.py     # prints proc-456 immediately
fcloud logs s-abc123 proc-456 --follow         # watch it live
fcloud wait s-abc123 proc-456 --timeout 0      # block; exits with its exit code

wait reads durable logs, so it works after the host is gone, survives checkpoint/migration, and exits 75 if a positive --timeout elapses while the process is still running. Prefer it over hand-rolled logs | grep loops — it distinguishes "still starting" from "already died".

The default log stream is combined (stdout + stderr) — prefer it. Most ML tooling (tqdm, HuggingFace/TRL) writes progress to stderr, so --stream stdout on a training job often looks empty even though it's producing output.

--emit-pid FILE (on spawn, exec, and run) writes {"session_id": ..., "process_id": ...} JSON to a file the instant the process starts — capture the handle even when stdout is piped or backgrounded. Never pipe a live exec through | tail: it buffers everything until EOF and swallows the detach line carrying the process id.

Sessions

CommandDescription
fcloud create [--sku SKU] [--min-disk-gb N] [--keep-warm SECONDS] [--no-checkpoint]Create a session (cold — $0 until used)
fcloud config <show|get|set|unset> [KEY] [VALUE]Per-user defaults, e.g. fcloud config set checkpoint off
fcloud sessions [SID] [--all] [--limit N]List sessions (--all includes cold/closed)
fcloud history <SID> [--limit N]Show a session's lifecycle timeline
fcloud stop <SID> [--wait] [--timeout SECONDS]Stop now: halt GPU spend, keep the files

Session lifecycle

A session's state is reported by fcloud sessions:

StateMeaning
hotRunning on a host and spending.
warmIdle but holding its host, still spending.
stoppingFinishing teardown.
coldStopped — $0, no host. Comes back online automatically the next time you target it.
preparingComing online (allocating a host / restoring state).

A freshly created session is cold and costs nothing until its first exec/run/upload. There is no resume command — targeting a cold session brings it back transparently, on a fresh host with its /workspace restored from cloud storage. fcloud stop exists only to halt spend immediately; files are preserved either way.

stop --wait blocks until the workspace manifest is durable — use it before anything that reads the manifest (fcloud ls, fcloud mount, fcloud map --from <sid>) rather than polling fcloud sessions yourself.

Preemption: checkpoint/restore, or rebuild anywhere

Sessions run on spot capacity. When a host is reclaimed, fcloud by default checkpoints the live sandbox (GPU state included) and restores it on a fresh host, so running processes continue. The restore is pinned to the region the checkpoint lives in, so it can wait for spot capacity there (awaiting_restore_capacity).

Pass --no-checkpoint to create, exec, run or shell to turn that off for a session: a preempted host then rebuilds it cold on any available host in any region — /workspace is kept, running processes are lost, and fcloud run/exec re-run your command (FCLOUD_MIGRATE_RESTART). Choose it when capacity matters more than in-flight state, ideally with a script that reloads its own checkpoints from /workspace or a volume.

fcloud run train.py --sku gpu_1x_h100 --no-checkpoint   # this session
fcloud config set checkpoint off                       # my default
fcloud config unset checkpoint                         # back to on

Precedence: --checkpoint/--no-checkpointFCLOUD_CHECKPOINT=on|offfcloud.json "checkpoint": falsefcloud config set checkpoint → on. The policy is fixed at create; --on <sid> keeps the session's own. Jobs never checkpoint. fcloud sessions marks opted-out sessions [no-checkpoint].

Credits

fcloud is prepaid: you buy credit, and sessions draw it down as they run. At $0 new work is refused, running sessions are closed and queued jobs are canceled — so the balance is worth knowing before a long run.

CommandDescription
fcloud creditsBalance, available, auto-refill and card
fcloud credits buy <usd> [--yes] [--no-autorefill]Buy credit through hosted checkout
fcloud credits autorefill on|off|set [--threshold USD] [--amount USD] [--daily-cap USD]Change the top-up rule
fcloud credits statement [--limit N]Recent ledger rows with a running balance

Balance vs available. balance is what has posted to your ledger; available subtracts usage that has been metered but not yet billed. Spend decisions use available.

Auto-refill charges a saved card when the balance falls below your threshold. It only fires when it is armed — enabled, consented to (which happens on your first purchase) and backed by a saved card — so fcloud credits reports armed rather than just on/off, and says which of those is missing. Turning it off warns that work stops at $0 and needs a confirmation.

fcloud credits buy opens a Stripe checkout page in your browser and waits for the payment to post; the card never passes through the CLI. On a machine with no browser the URL is printed to paste elsewhere.

Running out

A command refused for want of credit exits 4 and names the fix. It is not retryable — the same refusal repeats until credit lands (about a minute after it posts). Do not confuse it with exit 3 (a failed payment suspending the account) or exit 75 (a transient blip, which is worth retrying).

fcloud exec --sku gpu_1x_l4 python3 train.py
# Out of credits: insufficient credit: your prepaid credit is exhausted...
# Add credit with `fcloud credits buy 25`, or let a saved card top you up
# with `fcloud credits autorefill on`.
echo $?   # 4

Before provisioning, the CLI warns once when the balance is low and nothing will top it up. The warning goes to stderr and never blocks the run.

Files

CommandDescriptionNeeds active session?
fcloud upload [--on SID|SID] <local> [remote]Upload files into /workspace/Yes
fcloud download [--on SID|SID] <remote> [local]Download a fileNo — active or stopped
fcloud ls <SID> [path]List a stopped session's workspace manifestNo
fcloud mount <SID> <mountpoint>Mount a stopped session's files locally (read-only, via rclone)No

download uses the live host when the session is active (freshest bytes) and falls back to the saved manifest otherwise. ls and mount read the manifest the host writes when the session stops — use them after fcloud stop, the common "grab my outputs later" case.

A stopped session's durable logs appear under a virtual _logs/ directory:

fcloud ls s-abc123 _logs                  # build.log, session.log, exec-<pid>.log, ...
fcloud download s-abc123 _logs/build.log  # readable long after the session is gone

mount uses rclone's NFS mount (brew install rclone) — no macFUSE or kernel extension — and streams bytes directly from cloud storage. The mountpoint must not exist yet; Ctrl-C unmounts.

Volumes

Named, manifest-backed folders that move data between sessions — mount with --volume NAME[:MOUNT] on exec, run, shell, spawn, job run, and map. Full guide: Volumes.

fcloud volume create checkpoints
fcloud exec --sku gpu_1x_l4 --volume checkpoints:/workspace/ckpt python train.py
fcloud volume files checkpoints           # committed when the session detached
fcloud volume download checkpoints model.pt

Subcommands: create · list · files · download [-r] · cat · delete · import.

Batch

Run-to-completion jobs and durable fan-outs. Full guide: Batch: jobs & sweeps.

fcloud job run --sku gpu_1x_l4 --volume results -- python train.py
fcloud map --sku gpu_1x_l4 -- python train.py --lr {lr} ::: lr=1e-4,3e-4
fcloud sweeps                  # list sweeps
fcloud sweep status <name>     # counts + failures clustered by error

SSH & tunnels

fcloud shell is the fcloud-native PTY path — no SSH server required. fcloud ssh <sid> uses your local OpenSSH client over fcloud tunnel; if the image lacks sshd, the CLI bootstraps it on first connect (tens of seconds — bake openssh-server into the image to skip that). fcloud tunnel <sid> defaults to the session SSH port (2222) for use as a ProxyCommand; pass --port to tunnel any other TCP service inside the session:

ssh -o ProxyCommand="fcloud tunnel %h" root@s-abc123
fcloud tunnel s-abc123 --port 8000     # e.g. reach a vLLM server

Project config (fcloud.json)

Drop an fcloud.json in your project root (auto-discovered by walking up from cwd) to set defaults so you can skip the flags:

{
  "sku": "gpu_1x_l4",
  "volumes": [{ "name": "checkpoints", "mount": "/workspace/checkpoints" }],
  "image": {
    "base": "nvidia/cuda:12.8.1-devel-ubuntu24.04",
    "apt": ["git", "build-essential"],
    "pip": ["torch", "transformers"],
    "env": { "PYTHONUNBUFFERED": "1" }
  }
}

Explicit --sku overrides the file; --volume flags merge with (and re-map by name) the volumes defaults. Images are content-addressed — same config, same cache, instant startup.

Help

fcloud help            # grouped command list, concepts, recipes
fcloud help <command>  # full flags and semantics for one command

On this page