Skip to main content

gpumesh

Borrow your friends' GPUs. A distributed compute mesh that lets you share GPU power across machines on your network — with a single decorator, one CLI command, or a Python API.

PyPI version Python License Tests


What is gpumesh?

gpumesh turns multiple machines into a single, unified GPU compute pool. You start a coordinator on one machine, join workers from other machines (laptops, desktops, servers — anything with Python), and then run code across all of them as if they were one device.

 Machine A (coordinator)       Machine B (worker)       Machine C (worker)
 ┌─────────────────────┐      ┌──────────────────┐     ┌──────────────────┐
 │  RTX 4090           │      │  RTX 3080        │     │  T4              │
 │  Score: 120.5       │◄────►│  Score: 85.2     │◄───►│  Score: 12.0     │
 │                     │      │                  │     │                  │
 │  @accelerate(mesh)  │      │  receives task   │     │  receives task   │
 │  def train():       │      │  runs train()    │     │  runs train()    │
 └─────────────────────┘      └──────────────────┘     └──────────────────┘
          │
          ▼
    Results collected automatically

Use cases:

  • Hyperparameter search across multiple GPUs
  • Data preprocessing sharded across machines
  • Model training on a pool of consumer GPUs
  • Any embarrassingly parallel workload

Features

Transparent Acceleration

@accelerate(mesh)
def train(lr, epochs):
    return {"accuracy": 0.95}

# Single call → best local device
result = train(lr=0.01, epochs=100)

# Batch call → spread across ALL mesh devices
results = train.map([
    {"lr": 0.01, "epochs": 100},
    {"lr": 0.05, "epochs": 200},
])
Scenario What happens
Single call func(x) Runs on best local device (CPU/GPU)
Batch call func.map([...]) Spreads across all mesh devices
Mesh unreachable Falls back to local execution silently
GPUMESH_LOCAL=1 Forces local-only (no mesh)
GPUMESH_VERBOSE=1 Prints which device handled each task

Hardware Selection

Target specific GPU types:

@accelerate(mesh, gpu="A100")
def train(model):
    return model.cuda().forward(x)

Resource Specs

Declare what your task needs:

@accelerate(mesh, cores=8, memory="16GB", timeout=300)
def heavy_computation(data):
    return processed

Auto Device Placement

PyTorch models are automatically placed on the best device:

@accelerate(mesh)
def train_model(model, data):
    # model is automatically moved to the best GPU
    return model(data)

Fault Tolerance

  • Dead workers are detected and tasks are re-queued
  • Straggler workers are deprioritized
  • Graceful fallback to local execution if mesh is unavailable
  • Crash diagnostics on worker failures

Smart Scheduling

  • Benchmark scoring — each worker gets a 0-100 performance score
  • Memory-aware — tasks routed to workers with enough VRAM
  • Straggler deprioritization — slow workers get fewer tasks
  • TTL expiry — stale workers are automatically pruned

One-Line Setup

gpumesh setup    # Detects hardware, guides you through coordinator/worker
gpumesh quickjoin  # One-click: detect GPU and join mesh

Installation

Basic install

pip install gpumesh

With optional extras

pip install gpumesh[gpu]       # GPU detection + CUDA benchmarks (requires torch)
pip install gpumesh[tunnel]    # ngrok for public URLs
pip install gpumesh[sysinfo]   # System info (psutil)
pip install gpumesh[notebook]  # DataFrame support (pandas)
pip install gpumesh[ui]        # Beautiful setup wizard (rich + questionary)
pip install gpumesh[all]       # Everything above

Requirements

  • Python 3.9+
  • cloudpickle (automatically installed)
  • Optional: PyTorch for GPU detection and CUDA benchmarks

Quick Start

Step 1: Start a coordinator (one machine)

gpumesh setup

The wizard will:

  1. Detect your hardware (CPU cores, GPU model, VRAM)
  2. Ask if you want to be a coordinator or worker
  3. Generate a token and show connection info
  4. Display a live radar of connected workers

Or start directly:

gpumesh serve --port 8000 --token mysecret

Step 2: Join a worker (another machine)

gpumesh setup

Choose Worker and enter the coordinator's URL and token. Or:

gpumesh join http://coordinator-ip:8000 --token mysecret

Or use quickjoin (auto-detects GPU):

gpumesh quickjoin http://coordinator-ip:8000 --token mysecret

Step 3: Use your mesh

Option A: The @accelerate decorator (recommended)

from gpumesh import GPUMesh, accelerate

mesh = GPUMesh("http://coordinator:8000", token="mysecret")

@accelerate(mesh)
def train(lr, epochs):
    # Your code here — runs on all connected GPUs automatically
    return {"accuracy": 0.95}

# Single call → best local device
result = train(lr=0.01, epochs=100)

# Batch call → spread across all mesh devices
results = train.map([
    {"lr": 0.01, "epochs": 100},
    {"lr": 0.05, "epochs": 200},
])

Option B: The Python API

from gpumesh import GPUMesh

mesh = GPUMesh("http://coordinator:8000", token="mysecret")

# List connected workers
workers = mesh.workers()
print(workers)
# [{'id': 'w1', 'device': 'cuda', 'device_name': 'RTX 3080', 'score': 85.0}]

# Distribute a function across all workers
results = mesh.distribute(
    function=train_model,
    params=[
        {"lr": 0.01, "epochs": 100},
        {"lr": 0.05, "epochs": 200},
    ],
)

# Convert to DataFrame (optional)
df = mesh.results_to_dataframe(results)

Option C: CLI job submission

# Submit a Python script with payloads
gpumesh submit train.py --payloads payloads.json --wait

# Check status
gpumesh status JOB_ID

# Cancel if needed
gpumesh cancel JOB_ID

CLI Commands

Server & Connection

Command Description
gpumesh setup Interactive setup wizard — detects hardware, guides coordinator/worker choice
gpumesh serve [--port 8000] [--token SECRET] Start coordinator server
gpumesh join URL [--token SECRET] Join mesh as a worker
gpumesh quickjoin [URL] --token TOKEN One-click: detect GPU and join mesh
gpumesh worker --token TOKEN Start a worker that broadcasts and waits to be claimed
gpumesh radar Scan for nearby gpumesh devices on the network
gpumesh show-connection Show saved URL and token (for sharing)
gpumesh disconnect Clear saved connection

Job Management

Command Description
gpumesh submit SCRIPT --payloads FILE [--wait] Submit a Python script as a job
gpumesh status JOB_ID Check job progress and results
gpumesh cancel JOB_ID Cancel a running job
gpumesh kill [--force] Kill all gpumesh tasks

Monitoring

Command Description
gpumesh workers List connected workers with status
gpumesh devices Show all GPUs as one unified pool

Useful Flags

Flag Description
--port PORT Server port (default: 8000)
--token SECRET Authentication token
--url URL Coordinator URL (or set GPUMESH_URL env var)
--token SECRET Auth token (or set GPUMESH_TOKEN env var)
--tailscale Use Tailscale for encrypted network access
--no-discovery Disable UDP auto-discovery
--timeout SECONDS Per-task timeout (default: 240)
--wait Wait for job completion after submit

Python API

GPUMesh Client

from gpumesh import GPUMesh

mesh = GPUMesh("http://coordinator:8000", token="mysecret")

Listing Workers

workers = mesh.workers()
# [
#     {'id': 'w1', 'device': 'cuda', 'device_name': 'RTX 3080', 'hostname': 'laptop-a', 'score': 85.0, 'alive': True},
#     {'id': 'w2', 'device': 'cuda', 'device_name': 'T4', 'hostname': 'server-b', 'score': 12.0, 'alive': True},
# ]

Distributing Functions

def train_model(lr, epochs):
    # Your training code
    return {"accuracy": 0.95, "lr": lr}

results = mesh.distribute(
    function=train_model,
    params=[
        {"lr": 0.01, "epochs": 100},
        {"lr": 0.05, "epochs": 200},
        {"lr": 0.1, "epochs": 300},
    ],
    timeout=600,  # optional, default 300s
)
# [{'accuracy': 0.95, 'lr': 0.01}, {'accuracy': 0.95, 'lr': 0.05}, ...]

Device Access

# List all devices (local + remote)
devices = mesh.devices()
# [{'index': 0, 'hostname': 'laptop-a', 'device': 'cuda', 'device_name': 'RTX 3080', 'score': 85.2, 'status': 'alive'}]

# Count GPUs
count = mesh.device_count()  # 2

# Total compute score
total = mesh.total_score()  # 205.7

# Auto-pick best device
best = mesh.auto_device()

Job Management

# Submit a raw job
job_id = mesh.submit(name="preprocess", script="process.py", payloads=[{"file": "data.csv"}])

# Check status
status = mesh.status(job_id)
# {'finished': False, 'counts': {'pending': 0, 'running': 2, 'done': 1}, 'tasks': [...]}

# Convert results to DataFrame
df = mesh.results_to_dataframe(results)

Starting Workers from Python

# Start a coordinator (non-blocking, runs in background thread)
GPUMesh.start_coordinator(port=8000, token="mysecret")

# Join as a worker (non-blocking)
info = GPUMesh.add_worker("http://coordinator:8000", token="mysecret")

@accelerate Decorator

The @accelerate decorator makes your mesh resources transparent to your code.

Pattern 1: Basic Usage

from gpumesh import GPUMesh, accelerate

mesh = GPUMesh("http://coordinator:8000", token="mysecret")

@accelerate(mesh)
def preprocess(chunk_id, data_path):
    import pandas as pd
    df = pd.read_parquet(data_path)
    return {"chunk": chunk_id, "rows": len(df)}

# Single call → runs locally on best device
result = preprocess(chunk_id=0, data_path="data.parquet")

# Batch call → spreads across ALL mesh devices
results = preprocess.map([
    {"chunk_id": 0, "data_path": "part0.parquet"},
    {"chunk_id": 1, "data_path": "part1.parquet"},
])

Pattern 2: Hardware Selection

@accelerate(mesh, gpu="A100")
def train(model):
    return model.cuda().forward(x)

Pattern 3: Resource Specs

@accelerate(mesh, cores=8, memory="16GB", timeout=300)
def heavy_computation(data):
    return processed

Pattern 4: Global Install (Import Hook)

from gpumesh import GPUMesh, accelerate

mesh = GPUMesh("http://coordinator:8000", token="mysecret")
accelerate.install(mesh)  # Set global mesh

# Now all @accelerate functions auto-use the mesh
@accelerate  # No parentheses needed
def train(lr, epochs):
    return {"accuracy": 0.95}

Pattern 5: Binding to a Device

@accelerate(mesh)
def predict(x):
    return model(x)

# Bind to a specific device
gpu_predict = predict.to("cuda")
result = gpu_predict(x)

Pattern 6: Mesh Fallback

# If the mesh is unreachable, @accelerate falls back to local execution
@accelerate(mesh)
def train(lr, epochs):
    return {"accuracy": 0.95}

# This works even if the coordinator is down — runs locally
result = train(lr=0.01, epochs=100)

Network Options

Method Setup Best For Encrypted
LAN None Same Wi-Fi, fastest No
Tailscale Install Tailscale Remote teams, encrypted Yes
ngrok pip install gpumesh[tunnel] Public access, demos Yes

LAN (Default)

No setup required. Workers discover the coordinator automatically on the same network via UDP broadcast.

# Coordinator
gpumesh serve --port 8000

# Worker (on another machine)
gpumesh join http://192.168.1.10:8000 --token mysecret

Tailscale

Encrypted tunnel across the internet. Both machines need Tailscale installed.

# Coordinator
gpumesh serve --port 8000 --tailscale

# Worker
gpumesh join http://tailscale-ip:8000 --token mysecret

ngrok

Public URL for demos and testing.

# Coordinator
gpumesh serve --port 8000 --public
# Prints: ngrok tunnel → https://abc123.ngrok.io

# Worker
gpumesh join https://abc123.ngrok.io --token mysecret

How It Works

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      Coordinator                             │
│                                                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │ Job Queue │  │ Task DB  │  │ Workers  │  │ Events   │    │
│  │          │  │ (SQLite) │  │ Registry │  │ Log      │    │
│  └────┬─────┘  └──────────┘  └────┬─────┘  └──────────┘    │
│       │                           │                          │
│       └───────────┬───────────────┘                          │
│                   │                                          │
│          HTTP API (port 8000)                                │
└───────────────────┼──────────────────────────────────────────┘
                    │
        ┌───────────┼───────────┐
        │           │           │
   ┌────▼────┐ ┌────▼────┐ ┌────▼────┐
   │ Worker  │ │ Worker  │ │ Worker  │
   │ RTX4090 │ │ RTX3080 │ │ T4      │
   │ Score:  │ │ Score:  │ │ Score:  │
   │ 120.5   │ │ 85.2    │ │ 12.0    │
   └─────────┘ └─────────┘ └─────────┘

Job Flow

  1. Submit — Client sends a job (Python script + payloads) to the coordinator
  2. Queue — Coordinator splits payloads into tasks and stores them in SQLite
  3. Claim — Workers pull tasks based on their benchmark score and available memory
  4. Execute — Each task runs in an isolated subprocess on the worker
  5. Report — Workers report results back to the coordinator
  6. Collect — Client polls for results or waits for completion

Benchmark Scoring

Each worker runs a benchmark on join and gets a 0-100 score:

Score Range Typical GPU Use Case
80-100 RTX 4090, A100 Heavy training, large models
50-80 RTX 3080, RTX 3090 Medium training, inference
20-50 RTX 3060, T4 Light tasks, preprocessing
0-20 CPU only Very light tasks

Security

Token Authentication

All communication is authenticated with a shared token:

# Coordinator generates a token
gpumesh serve --token mysecret

# Workers must provide the same token
gpumesh join http://coordinator:8000 --token mysecret

Security Model

Feature Status
Token authentication All API requests
Rate limiting 5 failed attempts, then blocked
Process isolation Tasks run in separate subprocesses
File permissions Tokens stored with restricted permissions (0o600)

Important

Workers execute code from the coordinator. Only share your URL and token with people you trust. gpumesh is designed for trusted networks (home labs, team clusters).

  • Code execution: Function tasks (@accelerate) execute in the worker's process
  • Plaintext HTTP: All communication uses HTTP. Use Tailscale for encrypted tunnels
  • No sandbox: Tasks have full access to the worker machine
  • Token secret: Keep it secret. Rotate if compromised

Troubleshooting

Problem Fix
command not found: gpumesh Use python -m gpumesh instead, or check your PATH
401 bad token Ensure coordinator and worker use the same token
coordinator unreachable Check firewall rules and that coordinator is running
task timed out Increase --timeout 600 or split into smaller tasks
No connection could be made Windows: run gpumesh serve as Administrator for firewall rules
Worker not showing on coordinator Check both machines are on the same network; try gpumesh radar
ModuleNotFoundError: torch Install GPU extras: pip install gpumesh[gpu]
Worker crashes on submit Check worker logs; increase --timeout for long tasks
UDP broadcast not working Use direct gpumesh join URL instead of auto-discovery

Enable Verbose Logging

GPUMESH_VERBOSE=1 gpumesh serve
GPUMESH_VERBOSE=1 gpumesh join http://coordinator:8000 --token mysecret

Force Local-Only Mode

GPUMESH_LOCAL=1 python my_script.py

Limitations

  • Python only — Tasks must be Python functions or scripts
  • No GPU memory sharing — Each task gets its own process
  • No model sharding — Each task runs on one machine at a time
  • Single coordinator — Single point of failure (use Tailscale for reliability)
  • No built-in encryption — Use Tailscale for encrypted tunnels

Development

Contributing

git clone https://github.com/Samurai007AK/gpumesh.git
cd gpumesh
pip install -e ".[dev]"
pytest

Running Tests

pytest                    # Run all tests
pytest tests/test_api.py  # Run specific test file
pytest -v                 # Verbose output

Building

python -m build
twine check dist/*

License

MIT License. See LICENSE for details.

Release files for gpumesh 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gpumesh 0.9.0
File Size Uploaded
gpumesh-0.9.0.tar.gz 127.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gpumesh 0.9.0
File Interpreter ABI Platform
gpumesh-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 210.9 kB

Release files / gpumesh-0.9.0.tar.gz

Download URL gpumesh-0.9.0.tar.gz
Size 127.2 kB
Tags Source
SHA-256 checksum
How to use checksums
07c3fbaa9f9106d55fa66c4db162b7009ecff27464f738f28ba3395921a9701a
BLAKE2b-256 checksum
How to use checksums
040c7a929a21f2d5490fc6a3323582495b5730e14fc39deb150b83c706f38438
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release files / gpumesh-0.9.0-py3-none-any.whl

Download URL gpumesh-0.9.0-py3-none-any.whl
Size 83.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bc299a1842b480cba54e4ab698f62690df9734223cb25b2a079c5a7c74584c38
BLAKE2b-256 checksum
How to use checksums
3c535b3fcc11a0fb6a97b02cf5d5ebf05a96d5a2baecd47ce339edb6a4aa4e90
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.9

Release history Release notifications | RSS feed

3.2.0

2 release files

3.0.0

2 release files

2.0.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

This release

0.9.0 This release

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.7.4

2 release files

0.7.3

2 release files

0.7.2

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release 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