Skip to main content

FoxyGPU

Tests

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 --cmd value, exactly as above, in PowerShell, bash, or zsh — all three treat single quotes as a literal string, so $PORT and && reach the remote command unchanged. This does not work in cmd.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, write foxygpu.yaml by hand.
  • runtime: in foxygpu.yaml only supports colab today. 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 run just 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 launch if you closed the tab) and foxygpu connect again 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 committed notebook/FoxyGPU_Runner.ipynb embeds a copy of it — regenerate that file with foxygpu notebook notebook/FoxyGPU_Runner.ipynb and commit it after changing the agent, since (unlike --gist, which always embeds the current source) the default launch opens 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 $PORT in your --cmd (see Usage) and the agent hands you a free port automatically, preferring 9876 and 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 --cmd chain forever. Some CLI tools (Ollama's pull is 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. Since foxygpu run chains commands with &&, a hung one blocks everything after it. If a step seems stuck, check whether it actually finished (e.g. via a second foxygpu run with a quick status-checking command) before assuming it's slow — the fix is usually prefixing that one command with TERM=dumb.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

foxygpu-0.2.0.tar.gz (37.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

foxygpu-0.2.0-py3-none-any.whl (27.2 kB view details)

Uploaded Python 3

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

Hashes for foxygpu-0.2.0.tar.gz
Algorithm Hash digest
SHA256 39c3206ab7b18d5f43924b68043b633535e5a6fd383263b0f6a6b814f33267d2
MD5 1b129cdb8e2b51b30cb7cd9a8c7bb20c
BLAKE2b-256 c44170f1b77925b640009ae994b1cab60f7a66169b4ced81e78de423c6a1be53

See more details on using hashes here.

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

Hashes for foxygpu-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f9b5373eaec4ca4b64ef3689794640575ce3bdb6b893a7ee60650f3b36194795
MD5 20b3837426acaadd9bc10f6714d174b4
BLAKE2b-256 9cb6daa97dce0406f3164405adfc512f746608263cb4b2783a552f959b01c7cb

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.1

2 files

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page