Back

From Colab to Brev: A Practical GPU Development Cheatsheet


Most GPU projects follow the same path: prototype quickly, validate correctness, then move to a more persistent environment when compute, VRAM, or iteration speed becomes a bottleneck.

Google Colab is convenient for notebook-based experiments and quick CUDA builds. Brev — the cloud option used in my own workflow, not the only option — provides access to configurable cloud GPU instances that you can manage from a terminal or IDE.

This guide combines two practical cheatsheets: one for identifying GPUs, building CUDA/C++ projects, and managing Colab's ephemeral runtimes; the other for searching, provisioning, and operating Brev GPU instances. The commands are designed to be copied, adapted, and tested without assuming a particular project repository.

Mental model in one paragraph

Colab is an ephemeral notebook VM: free tier is usually a T4, Pro can give you L4/A100, there is no Docker daemon, and the filesystem resets on recycle. Brev is the opposite: you pick an exact GPU type, VRAM, provider and boot time, create a persistent box, SSH into it, and pay per hour. Prototype on Colab, scale on Brev.

Tested with

Colab GPU runtime (verify with !nvidia-smi and !nvcc --version — assignment varies), CMake 3.24+ for CMAKE_CUDA_ARCHITECTURES=native, Brev CLI v0.6.335 verified October 11, 2026. Brev flags and inventory change over time — run brev search --help and brev create --dry-run to confirm behavior on your version.

1. Choosing: Colab vs Brev

Colab Brev
Best for Free prototyping, notebooks, quick CUDA builds Training, large VRAM, persistent boxes, multi-GPU
GPU choice Assigned (T4 common, A100/L4 not guaranteed) You choose, e.g.: T4, L4, A10G, A100, H100, L40S, RTX — examples only, inventory varies
Lifetime Ephemeral; runtime limits and idle timeouts vary Persistent until you stop/delete; stopping may end compute charges while attached storage and other resources can continue to incur costs — check pricing and storage-retention policy
Access Browser notebook, !cmd shell SSH, VS Code / Cursor, port-forward, managed ports
Cost control Quotas and disconnects --dry-run for preview plus check of actual hourly price and disk config, per-hour pricing, stop when idle

My default flow: validate the idea and the CUDA build on Colab, then reproduce it on a Brev box with the VRAM the model actually needs.

2. Colab: Enable and Identify the GPU

In the Colab UI:

Runtime → Change runtime type → Hardware accelerator: GPU → Save

Then confirm what you actually got — never assume T4. GPU assignment and installed CUDA tooling are separate checks: a GPU runtime does not guarantee nvcc is installed.

!nvidia-smi
!nvidia-smi --query-gpu=name,driver_version,memory.total,compute_cap --format=csv
!nvcc --version || echo "nvcc not installed on this runtime"
!python3 --version
import torch
print(torch.__version__)
print(torch.cuda.is_available())
print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else "no cuda")

TensorFlow / JAX alternates:

import tensorflow as tf; print(tf.config.list_physical_devices('GPU'))
import jax; print(jax.devices())

3. CUDA Architectures (Applies to Both)

A GPU architecture mismatch can cause no kernel image available at runtime; rebuild with the correct flag. Compute capability identifies hardware features, while CUDA toolkit and driver compatibility are separate constraints — matching the arch flag alone does not guarantee the toolkit/driver combination is supported. An illegal instruction error can have other causes, so check the surrounding error output before changing the architecture. Reference table:

GPU Compute Cap. CMake flag
T4 (Colab free tier)7.5-DCMAKE_CUDA_ARCHITECTURES=75
V1007.0-DCMAKE_CUDA_ARCHITECTURES=70
A1008.0-DCMAKE_CUDA_ARCHITECTURES=80
RTX 30xx / A10G / A408.6-DCMAKE_CUDA_ARCHITECTURES=86
L4 / L40S / RTX 40908.9-DCMAKE_CUDA_ARCHITECTURES=89
H1009.0-DCMAKE_CUDA_ARCHITECTURES=90
B100 / B200 (Blackwell DC)10.0-DCMAKE_CUDA_ARCHITECTURES=100
RTX 5090 (Blackwell GeForce)12.0-DCMAKE_CUDA_ARCHITECTURES=120

Auto-detect instead of guessing (works on Colab and on any Brev box):

# Fast bash one-liner (uses primary GPU):
ARCH=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader | head -n1 | tr -d '.')
echo "-DCMAKE_CUDA_ARCHITECTURES=${ARCH}"

Or via Python (multi-GPU safe, deduplicates capabilities):

%%bash
python3 - <<'PY'
import subprocess

result = subprocess.run(
    ['nvidia-smi', '--query-gpu=compute_cap', '--format=csv,noheader'],
    check=True, capture_output=True, text=True,
)
archs = sorted(set(result.stdout.strip().replace('.', '').splitlines()))
print('-DCMAKE_CUDA_ARCHITECTURES=' + ';'.join(archs))
PY

On CMake ≥ 3.24 you can also delegate detection entirely — but native detects the GPU visible at configure time, so it is for local development, not portable binaries:

!cmake -S . -B build -DCMAKE_CUDA_ARCHITECTURES=native
  • Local development: native — fastest iteration on the current GPU.
  • Known deployment target: explicit arch, e.g. -DCMAKE_CUDA_ARCHITECTURES=80 for A100.
  • Multiple deployment targets: deliberate list, potentially including PTX for forward compatibility, e.g. -DCMAKE_CUDA_ARCHITECTURES="75;80;90a".

For the full story on native vs all-major vs explicit lists, see my CMake CUDA Architecture Configuration guide.

Brev capability shortcut

Brev search exposes the same architecture as a filter: --min-capability 7.5 (Turing+), 8.0 (Ampere+), 8.9 (Ada+), 9.0 (Hopper and newer). Use it when you care about features (e.g. FP8, Transformer Engine) rather than a specific card name.

4. Colab Shell Basics and Python GPU Packages

Two rules: !cmd runs shell, %cd dir persists directory (!cd does not). Colab idle timeouts and maximum runtime vary with availability and usage.

!pwd && ls
%cd /content
!apt-get update -qq && apt-get install -y -qq cmake ninja-build
!cmake --version

First inspect what is already installed — Colab runtimes often ship with PyTorch:

!nvidia-smi --query-gpu=name,driver_version,memory.total --format=csv
!python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.is_available())"

Optional pinned environment (example only — match the wheel channel to your runtime and project; this can replace pre-installed packages):

!pip install -q "torch==2.3.1" "torchvision==0.18.1" --index-url https://download.pytorch.org/whl/cu121
!pip freeze | grep -Ei "torch|transformers|accelerate|datasets|cuda"
!python -c "import torch; print(torch.__version__, torch.version.cuda, torch.cuda.get_device_name(0))"

For a reproducible project, record pip freeze output plus nvidia-smi driver, nvcc --version, and cmake --version alongside the code.

Free VRAM before big jobs (empty_cache() releases unused cached allocations only — it does not free memory held by live tensors):

import torch
torch.cuda.empty_cache()
!nvidia-smi --query-gpu=memory.used,memory.total --format=csv

5. Colab: Build Any CUDA C++ Project

Generic recipe — replace the arch flag with the value from Section 3:

!git clone <repo-url>
%cd /content/<repo>
!cmake -S . -B build -DCMAKE_CUDA_ARCHITECTURES=75 -DCMAKE_BUILD_TYPE=Release
!cmake --build build -j$(nproc)
!./build/<binary>

Iterate on a single target instead of rebuilding everything:

!cmake --build build --target <target> -j$(nproc)

If the build gets killed mid-way, -j$(nproc) is usually too parallel for the available RAM — retry with -j2.

Validate against hidden env dependencies with a clean build or fresh clone before calling it done:

!rm -rf build && cmake -S . -B build -DCMAKE_CUDA_ARCHITECTURES=75 -DCMAKE_BUILD_TYPE=Release && cmake --build build -j2
# or: fresh clone in /tmp and build there to catch files only present in /content

6. Colab: Monitor, Persist, and Survive Resets

Live monitoring:

!nvidia-smi -l 1        # live (Ctrl-C to stop)
!nvidia-smi dmon -s u   # util stream
!which ncu nsys nvprof || echo "no profilers on this runtime"

In-code timing (CUDA events / chrono around kernels) beats missing ncu on most Colab runtimes.

The filesystem resets. Build in /content (fast), store on Drive (durable):

from google.colab import drive
drive.mount('/content/drive')
!cp -r /content/drive/MyDrive/<src> /content/work
!df -h /content
!free -h

Drive is durable but slow — copy datasets and checkpoints to /content once per session and work there; avoid repeatedly copying multi-GB files in a loop if I/O becomes the bottleneck.

Quotas and resets:

!df -h
!free -h
# Runtime → Disconnect and delete runtime → factory reset when stuck

Expect on Colab

Idle disconnects, runtime limits, and GPU assignment vary. Free-tier notebooks can run for up to 12 hours depending on availability and usage; A100/L4 access is not guaranteed. If files vanished, the runtime recycled — re-clone and restore from Drive.

8. Brev: Filter by Model, VRAM, and Provider

Partial, case-insensitive match on GPU name. Models below are examples of what may be available — not guaranteed inventory:

brev search --gpu-name T4       # budget 16GB
brev search --gpu-name L4       # efficient 24GB
brev search --gpu-name A10G     # balanced 24GB (e.g. g5.xlarge)
brev search --gpu-name A100     # 40/80GB training
brev search --gpu-name H100     # Hopper 80GB
brev search --gpu-name L40S     # 48GB Ada
brev search --gpu-name RTX      # RTX4090 / RTX6000Ada / RTX5090
brev search --gpu-name A4000    # cheap 16GB (e.g. hyperstack_A4000 — price varies, checked Oct 2026)
brev search --gpu-name A6000    # 48GB Ampere

Filter by VRAM, provider, disk, boot time, and features. --min-vram is the minimum per GPU; --min-total-vram is the sum across devices (COUNT x VRAM) — not a single unified pool. An 80 GB aggregate does not allow a single 80 GB allocation on one GPU:

brev search --min-vram 16 --sort price        # per-GPU minimum
brev search --min-vram 24 --sort price
brev search --min-vram 40 --sort price
brev search --min-vram 80 --sort price        # A100-80GB / H100
brev search --min-total-vram 48 --sort price  # COUNT x VRAM (multi-GPU), e.g. 2x24GB
brev search --min-total-vram 160 --sort price # e.g. 2x80GB — per-GPU limits still apply

brev search --provider aws --sort price
brev search --provider gcp --sort price
brev search --gpu-name A100 --provider aws --sort price

brev search --min-disk 500                     # GB
brev search --max-boot-time 3 --sort price     # fast boot
brev search --max-boot-time 5 --sort price
brev search --stoppable --sort price
brev search --rebootable --sort price
brev search --flex-ports --sort price

Compute capability reference (same arch table as Section 3, Brev view):

Cap Arch GPUs
6.1PascalP4 (avoid for new work)
7.5TuringT4
8.0AmpereA100, A10
8.6AmpereA10G, A40, A4000, A6000, RTX30xx
8.9AdaL4, L40/L40S, RTX4090, RTX6000Ada
9.0HopperH100
10.0Blackwell DCB100/B200
12.0BlackwellRTX 5090
brev search --min-capability 7.5 --sort price  # Turing+
brev search --min-capability 8.0 --sort price  # Ampere+
brev search --min-capability 8.9 --sort price  # Ada+
brev search --min-capability 9.0 --sort price  # Hopper and newer

9. Brev: Preset Recipes and JSON + jq

Recipes I reuse:

# dev: fast + cheap
brev search --max-boot-time 3 --sort price | head -10

# ML training: high VRAM
brev search --min-vram 40 --sort price | head -10

# large model: total VRAM
brev search --min-total-vram 80 --sort price | head -10

# production: specific GPU + fast boot
brev search --gpu-name A100 --max-boot-time 5 --sort price

# budget: cheapest usable
brev search --min-vram 16 --sort price | head -5

# Hopper and newer
brev search --min-capability 9.0 --sort price

# stoppable + VRAM (cost-saving training)
brev search --stoppable --min-total-vram 40 --sort price

Scriptable search with JSON (field names vary by CLI version — inspect brev search --json | jq '.[0]' first):

brev search --json | jq '.[0]'
brev search --json | jq -r '.[0] | keys'
# example only — verify keys on v0.6.335 before scripting:
brev search --gpu-name A100 --json | jq -r '.[] | "\(.type) \(.price_per_hour)/hr"'

10. Brev: Create Instances Safely

brev create my-box                                   # smart defaults (~20GB VRAM, 500GB disk, cap 8.0+, <7m boot)
brev create my-box --type g5.xlarge                  # exact type
brev create my-box --type g5.xlarge,g5.2xlarge       # fallback chain
brev create my-box --gpu-name A100 --min-vram 40     # filters directly on create
brev create my-cluster --count 3 --type g5.xlarge    # multi (confirm first: cost x3)
brev create my-box --startup-script @setup.sh        # local file, @ prefix
brev create my-box --startup-script 'pip install torch'
brev create my-box --dry-run                         # preview match, no cost — always run first, then still check hourly price + disk
brev search --gpu-name A100 | brev create my-box     # pipe search -> create
brev search cpu --min-ram 64 | brev create my-cpu-box

Safety before create

Confirm first: delete, stop, --count >1, H100 / multi-GPU create (expensive). Always: --dry-run before create, then verify the actual hourly price and disk configuration in the search output — dry-run does not replace that check. Preview brev ls | awk ... before piping to stop/delete, show cost/type before create. Stopped instances may still accrue storage charges.

11. Brev: Daily Use — Shell, Copy, Ports, List

Shell and exec

brev shell my-box                      # interactive SSH
brev exec my-box "nvidia-smi"          # one-shot command
brev exec my-box @setup.sh             # run local script remotely
brev exec box1 box2 box3 "nvidia-smi"  # multi-instance

Open in editor

brev open my-box          # default editor
brev open my-box code     # VS Code
brev open my-box cursor
brev open my-box windsurf
brev open my-box tmux
brev open --set-default cursor

Copy files

brev copy ./train.py my-box:/home/ubuntu/
brev copy my-box:/home/ubuntu/ckpt/ ./ckpt/

Port-forward (simple)

brev port-forward my-box -p 8888:8888  # jupyter (-p / --port, same flag on v0.6.335)
brev port-forward my-box -p 8000:8000

Managed ports (needs P / flex-ports)

brev ports ls my-box
brev ports ls my-box --json
brev ports get nport-abc123
brev ports open my-box 8080
brev ports open my-box 8000-8031
brev ports open my-box 3000 --protocol http --public
brev ports open my-box 8888 --protocol http --authorize me@example.com
brev ports open my-box 53 --protocol udp --allow 203.0.113.10/32
brev ports update nport-abc123 --destination-port 8081
brev ports update nport-abc123 --public
brev ports remove my-box 8080
brev ports rm nport-abc123

Public ports are public

Managed ports with --public can expose services to the internet. Avoid publishing unauthenticated Jupyter servers or dev endpoints — prefer --authorize me@example.com or IP allow-lists, and close ports when done.

List / manage

brev ls = cloud instances only. brev ls nodes = Brev Connect machines only (never in brev ls).

brev ls
brev ls instances
brev ls nodes
brev ls orgs
brev ls --json | jq -r '.workspaces[].name'
brev ls nodes --json | jq -r '.[].name'
brev ls nodes --json | jq -r '.[] | select(.status=="Connected") | .name'

brev stop my-box
brev start my-box
brev delete my-box
brev reset my-box        # recover from errors
brev stop --all          # all running (confirm first)

Bulk (preview before destructive):

brev ls | awk '/RUNNING/ {print $1}'              # preview
brev ls | awk '/RUNNING/ {print $1}' | brev stop
brev ls | awk '/STOPPED/ {print $1}' | brev delete
brev ls | awk '/STOPPED/ {print $1}' | brev start
brev ls | grep "test-" | awk '{print $1}' | brev stop
brev ls | awk '/RUNNING/ {print $1}' | brev exec "nvidia-smi"
brev search --gpu-name A100 | brev create my-box | brev open cursor
brev create my-box | brev exec @setup.sh

Orgs:

brev org ls
brev org set my-org   # alias: brev set my-org
brev invite

Once the box is up, the Colab checks from Section 2 apply verbatim — nvidia-smi, compute-cap query, and the CMake arch flag for whatever GPU Brev gave you.

12. Troubleshooting Both

Colab

Symptom Fix
CUDA unknown / no GPURuntime → GPU type set? !nvidia-smi must list a GPU first
no kernel image availableCUDA-arch mismatch — rebuild with Section 3 flag for this GPU
CUDA error / toolkit-driver mismatchnvcc --version vs driver in nvidia-smi — align toolkit/channel, reinstall wheel if needed
illegal memory access / illegal instructionSeparate bug class — check indices/bounds, UVM, arch flag; do not assume arch fix alone
CUDA out of memorySmaller batch, release tensors, empty_cache() (cached-only), restart runtime
nvcc missingNormal on CPU runtime — switch to GPU runtime
cmake old/missingapt-get install -y cmake, need 3.24+ for native (3.20+ minimum for most CUDA projects otherwise)
Build killed mid-way-j$(nproc) too parallel for RAM — try -j2; keep tab active
Profilers absentwhich ncu check; fall back to in-app timing
Files vanishedRuntime recycled — re-clone, use Drive for durable storage

Brev

brev ls                # does it exist? RUNNING?
brev ls nodes          # Connect machines live here, not in brev ls
brev org ls            # wrong org?
brev refresh           # fix SSH config
brev healthcheck
which code cursor      # editor in PATH?
brev feedback "what failed, what you expected"  # no secrets
Symptom Fix
Instance not foundbrev ls + brev ls nodes + brev org ls
Create failsTry another type: brev search --sort price; check quota
SSH failsbrev refresh, instance must be RUNNING
Editor won't openwhich code, brev open --set-default code

13. Cross-Environment Reproducibility Checklist

The move from Colab to Brev only pays off if the same project builds and behaves consistently in both. Run this before scaling:

  • Record GPU model, compute capability (nvidia-smi --query-gpu=compute_cap), driver (nvidia-smi), and CUDA toolkit (nvcc --version).
  • Pin compiler, CMake (3.24+ for native), and Python deps (pip freeze).
  • Keep build configuration and architecture selection in a script or CMake preset — not a pasted shell line.
  • Run the same correctness tests on both environments.
  • Benchmark with warm-up iterations and CUDA events or another appropriate GPU timing method.
  • Save test outputs, benchmark results, and checkpoints outside ephemeral storage (Drive on Colab, persistent disk/snapshot on Brev).

Conclusion

Colab minimizes setup friction; Brev provides a more persistent environment with configurable GPU resources. The useful skill is knowing when to move between them — and how to keep builds, tests, and benchmarks reproducible across both.

Identify the GPU, select an appropriate CUDA architecture, pin the environment, validate correctness, and measure performance before scaling up. Treat cloud costs and persistent storage as part of the engineering workflow, not an afterthought.

For production-oriented CUDA development, pair this cheatsheet with the CMake CUDA architecture guide to understand native builds, explicit architecture targets, and portable deployment.