FoxyGPU
Run local code — FastAPI backends, frontend dev servers, or anything else — on Google Colab's free-tier GPU, driven entirely from your own machine.
How it works
foxygpu launch opens FoxyGPU's own runner notebook directly in Colab — no
manual notebook upload, and no GitHub account or token needed. The notebook
is identical for every user (nothing personalized is baked in), so it's just
committed straight into this repo and Colab loads it from there; Colab can open
any public GitHub file with zero authentication. That notebook starts a
control-plane agent, reached from your machine over a Cloudflare
Tunnel quick tunnel (no account needed).
The local foxygpu CLI talks to that agent to upload your project, start it with a
shell command, stream its logs, and expose whatever port it's listening on with its
own public URL.
┌───────────────────┐
│ foxygpu CLI │
│ (local machine) │
└───────────────────┘
│
│ HTTPS/WSS via a Cloudflare Tunnel
│ (no account needed)
▼
┌──────────────────────────────┐
│ foxygpu_agent │
│ (Colab VM, GPU runtime) │
│ │
│ spawns your process │
│ (uvicorn / npm / anything) │
└──────────────────────────────┘
Every agent endpoint requires a bearer token generated at startup — the tunnel URL alone isn't enough to run anything on your VM.
Install
Everything — the CLI and the Colab agent it deploys — ships as one Python package:
pip install -e .
Setup
1. Launch the Colab runtime
foxygpu launch
This just opens Colab straight to FoxyGPU's own committed notebook — nothing to sign in to, no token, no account needed.
In the browser: select a GPU runtime (Runtime > Change runtime type > GPU),
run all cells. The last cell prints a foxygpu connect ... command — copy it.
Prefer not to open a link we host at all? foxygpu notebook ./FoxyGPU_Runner.ipynb
writes the same notebook to a local file so you can read it yourself and upload
it to Colab manually (File > Upload notebook) — zero network calls to anything
but Colab itself.
If you've modified foxygpu/agent_source.py locally and want the one-click
experience for your own version without forking/hosting a repo, foxygpu launch
--gist publishes your copy to a GitHub Gist instead — that path does need a
classic GitHub token with the gist scope (fine-grained tokens don't support
the Gists API and fail with a 404); create one at https://github.com/settings/tokens
-> "Generate new token (classic)".
2. Connect
Paste the command Colab printed, e.g.:
foxygpu connect https://xxxx.trycloudflare.com --token <token>
Usage
Run a project (any language/framework — it's just a shell command). The agent
picks a free port for you and injects it as $PORT — reference that instead of
a literal number so you never have to think about which ports are free or
reserved:
foxygpu run ./my-fastapi-app --cmd 'pip install -r requirements.txt && uvicorn main:app --host 0.0.0.0 --port $PORT' --expose
Shell note: use single quotes around the
--cmdvalue, exactly as above, in PowerShell, bash, or zsh — all three treat single quotes as a literal string, so$PORTand&&reach the remote command unchanged. This does not work incmd.exe(no concept of single-quoted literal strings, and it interprets&&itself) — use PowerShell or a bash-like shell instead.
One-command deploy (foxygpu.yaml)
Once you know your --cmd, save it to a foxygpu.yaml in your project so you
don't have to retype it:
runtime: colab # only "colab" works today — see the multi-runtime issue
gpu: true
command: pip install -r requirements.txt && uvicorn main:app --host 0.0.0.0 --port $PORT
Then just:
foxygpu deploy
deploy behaves like redeploy (stops the previous deployment of the same
project first) and exposes automatically by default. If there's no
foxygpu.yaml yet, it tries to auto-detect one for you — a FastAPI/Flask app
next to requirements.txt, or a Vite/Next.js/generic npm project next to
package.json — writes it, and deploys. If nothing's recognized, it tells you
so rather than guessing; write the file yourself or use run/redeploy with
an explicit --cmd instead.
Logs stream live, and the CLI prints which port got assigned. --expose
immediately opens a public tunnel once the process starts and prints the URL.
If you skip it, expose later — with no argument it defaults to the most
recently started process's port:
foxygpu expose
Databases, external APIs, and secrets
An externally-hosted database or API (a hosted Postgres, a third-party
API, an OAuth provider) just works — the Colab VM has normal outbound
internet access, no different from any other server. A local-only
database your app points at via localhost won't be reachable from the VM;
either run it on the VM too as part of your command (state is wiped when the
session ends) or point at an externally-hosted instance instead.
For secrets (API keys, DB passwords) — don't embed them with
export SECRET=x && ... in --cmd: the full command is stored and shown
verbatim in foxygpu status and echoed as the first line of streamed logs.
Instead, use --env/--env-file, which inject them directly as process
environment variables without ever appearing in --cmd, status, or the logs
(only the variable names are ever shown, never the values):
foxygpu run ./my-app --cmd 'uvicorn main:app --host 0.0.0.0 --port $PORT' --env DATABASE_URL=postgres://... --env-file .env
--env (repeatable) and --env-file (a .env-style KEY=VALUE file) both
work on run, redeploy, and deploy; --env wins on a conflicting key.
In foxygpu.yaml, use env_file: .env for the same thing — avoid putting
real secret values directly under an inline env: mapping in a file you
commit to git; env_file should point at a local, gitignored file instead.
One more gotcha specific to this tool: if your app does OAuth login, the
callback URL is normally registered as a fixed value with the provider.
FoxyGPU's exposed URL is a fresh random *.trycloudflare.com address on every
expose/redeploy, which breaks flows expecting a stable callback URL — not
something to work around today, just worth knowing going in.
Edited your code and want to update what's running? foxygpu run always
starts a fresh, separate deployment — it won't stop whatever's already running
first. Use redeploy instead, which stops the previous deployment of the same
project (matched by directory name, or --name if you gave one) before
starting the new one:
foxygpu redeploy ./my-fastapi-app --cmd 'pip install -r requirements.txt && uvicorn main:app --host 0.0.0.0 --port $PORT' --expose
If the new run lands back on the same port — likely, since stopping the old
one just freed it — an existing exposed URL for that port keeps working
automatically, no need to expose again.
Check GPU status and running processes (including their assigned ports):
foxygpu status
Stream logs for a process, reconnect after detaching, or stop it (add --all
to stop everything):
foxygpu logs <process-id>
foxygpu stop <process-id>
foxygpu stop --all
Pressing Ctrl+C while logs are streaming only detaches your terminal — the
remote process keeps running on Colab. The CLI reminds you of the logs/stop
commands above when you do this.
More examples
Node.js app (read process.env.PORT in your server code):
foxygpu run ./my-node-app --cmd 'npm install && node server.js' --expose
Frontend dev server (Vite/React/etc.):
foxygpu run ./my-frontend --cmd 'npm install && npm run dev -- --host 0.0.0.0 --port $PORT' --expose
One-off script or training job (no server, so skip --expose):
foxygpu run ./train-job --cmd 'pip install -r requirements.txt && python train.py'
See foxygpu run --help for this same set of examples from the CLI.
Full working example
examples/ollama-chat is a complete ChatGPT-style app (FastAPI backend + a small frontend) that runs a real GPU-backed Ollama model on Colab — a good first thing to deploy to confirm your setup end-to-end.
Excluding files from upload
By default .git, node_modules, __pycache__, venv/.venv, and a few build
directories are excluded when zipping your project. Add more patterns by copying
.foxygpuignore.default to .foxygpuignore in your project
root.
Development
The test suite runs a real instance of the agent locally (no Colab needed) and
drives it over HTTP/WebSocket, plus in-process CLI tests via Typer's test
runner. It never touches your real ~/.foxygpu/config.json — every test gets
an isolated one automatically.
pip install -e ".[dev]"
pytest
Known limitations
foxygpu deploy's auto-detection is deliberately limited (FastAPI/Flask + requirements.txt, or Vite/Next.js/generic npm + package.json) — a confidently wrong guess is worse than admitting it doesn't recognize your project. When it doesn't, writefoxygpu.yamlby hand.runtime:infoxygpu.yamlonly supportscolabtoday. Kaggle/RunPod/ Lambda/local-GPU support is tracked as a future multi-runtime effort.- FastAPI is only used to build the agent itself (the control-plane server running
inside Colab) — it is not a requirement for what you deploy.
foxygpu runjust executes whatever shell command you give it via--cmd, so any language or framework the Colab VM can run works (Node, Go, Rust, Flask, Streamlit, a plain training script, anything), not just Python or FastAPI. - Colab free-tier sessions are ephemeral (idle timeout, ~12h cap). If the session
restarts, run the notebook again (re-run
foxygpu launchif you closed the tab) andfoxygpu connectagain with the new URL/token. - The control URL and token grant code execution on the VM — don't share them.
foxygpu launch --gist(the opt-in path) publishes to a public Gist (Gist API has no private-but-linkable option) — it contains no secrets (the agent's token is generated fresh at runtime in Colab, not baked into the notebook), but anyone who finds the Gist URL can see and re-run it against their own Colab.- The agent source lives at
foxygpu/agent_source.py. The committednotebook/FoxyGPU_Runner.ipynbembeds a copy of it — regenerate that file withfoxygpu notebook notebook/FoxyGPU_Runner.ipynband commit it after changing the agent, since (unlike--gist, which always embeds the current source) the defaultlaunchopens the version already committed to this repo. - Port 8765 is reserved — the agent itself listens there inside Colab. You
shouldn't need to think about this: reference
$PORTin your--cmd(see Usage) and the agent hands you a free port automatically, preferring9876and falling back to another free one if that's taken (e.g. a second concurrent project). - A command with an animated progress bar can hang your whole
--cmdchain forever. Some CLI tools (Ollama'spullis one — see examples/ollama-chat) never exit their progress renderer when run through a non-interactive pipe like the one the agent uses to capture output, even though the real work finishes. Sincefoxygpu runchains commands with&&, a hung one blocks everything after it. If a step seems stuck, check whether it actually finished (e.g. via a secondfoxygpu runwith a quick status-checking command) before assuming it's slow — the fix is usually prefixing that one command withTERM=dumb.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file foxygpu-0.2.0.tar.gz.
File metadata
- Download URL: foxygpu-0.2.0.tar.gz
- Upload date:
- Size: 37.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39c3206ab7b18d5f43924b68043b633535e5a6fd383263b0f6a6b814f33267d2
|
|
| MD5 |
1b129cdb8e2b51b30cb7cd9a8c7bb20c
|
|
| BLAKE2b-256 |
c44170f1b77925b640009ae994b1cab60f7a66169b4ced81e78de423c6a1be53
|
File details
Details for the file foxygpu-0.2.0-py3-none-any.whl.
File metadata
- Download URL: foxygpu-0.2.0-py3-none-any.whl
- Upload date:
- Size: 27.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.11.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9b5373eaec4ca4b64ef3689794640575ce3bdb6b893a7ee60650f3b36194795
|
|
| MD5 |
20b3837426acaadd9bc10f6714d174b4
|
|
| BLAKE2b-256 |
9cb6daa97dce0406f3164405adfc512f746608263cb4b2783a552f959b01c7cb
|