jobd

io.github.musharnaPublished by GitHub account musharnav0.5.52Updated Oct 26, 2026

Self-hostable GPU-aware job broker: submit, route by VRAM, babysit jobs across machines via MCP.

Overview

AI-generated overview

MCP server for a self-hosted jobd broker that lets an assistant submit, route, monitor and preempt GPU jobs across your own machines.

What you can do
  • Submit jobs to the broker's queue, including command strings run through bash.
  • Inspect the queue and single jobs, and read captured job logs.
  • Cancel or preempt running jobs, and review event history.
  • List workers with their health and free capacity, and remove a worker from the fleet.
When to use it
Useful when you run a small fleet of your own machines with GPUs and want an assistant to queue training or batch jobs, route them to a worker with enough free VRAM, and babysit them over time instead of running long commands in a shell.
Requirements
A local Python 3.11+ environment with the jobd MCP extra installed (jobd-mcp), plus a reachable jobd broker. Set JOBD_URL to the broker base URL (defaults to and JOBD_API_TOKEN to the broker's bearer token unless the broker runs with JOBD_ALLOW_NO_AUTH=1. Network access to the broker is needed.
Before you install
The token in JOBD_API_TOKEN grants control of a broker that executes commands on your machines; keep the broker on loopback or a private tailnet, since no-auth mode on another bind exposes unauthenticated code execution. Submitted command strings run via bash. Preemption sends SIGTERM then SIGKILL and does not re-run the job, and worker removal changes the fleet.

Installation

In SourceWeft

  1. Open jobd in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

jobd

jobd is a job broker with an MCP server that lets AI agents submit, route and babysit jobs on a few machines of your own. Like task-spooler or pueue, but across all your machines on Tailscale or a LAN, routing each job to a worker with the VRAM free, with no cloud, Kubernetes or Slurm.

[CI] [PyPI] [Python] [License] [DOI] [Glama]

[jobd in action: job fleet status shows four workers and their versions; a GPU job routes to the worker with enough free VRAM and streams back; a stdin batch submits two jobs at once; job logs -f follows one to completion]

bash
# from any machine on your tailnet:job submit --project myproj --gpu --vram-required 16 --wait -- python train.py# → routed to whichever worker has ≥16 GB VRAM free, streamed back to your terminal

VRAM routing tracks one GPU per host: the worker reports free memory on GPU index 0 only.

Why it exists

  • VRAM-fit routing. Each job goes to a worker whose live free VRAM, RAM, CPUs, capability tags and arch/OS fit it, instead of you guessing which box is free.
  • Preemption with a checkpoint window. A higher-priority job can preempt a running one: SIGTERM, a grace window for the workload to save into its JOBD_CHECKPOINT_DIR, then SIGKILL. A preempted job is not re-run; to resume, submit a new job pointed at the checkpoint (docs/preemption.md).
  • Survives sessions. Jobs live in the broker, not your shell. Submit, close the laptop, check back tomorrow.
  • Agent-native. An MCP server lets an AI agent submit, monitor and babysit jobs as tool calls (benchmark).
  • Yours. One broker process on a machine you own. No accounts, no telemetry, no per-GPU-hour billing. Only the optional self-update scripts go online (runbook).

Why not just use…?

ToolWhat it gives youWhy jobd instead
nohup / tmux / sshRuns a command on one boxNo queue, VRAM-aware routing, preemption, or record of what ran where
task-spoolerA job queue on one machinejobd queues across all your machines and routes by live VRAM/CPU fit
PueueA mature single-machine queue daemonPueue's README puts distributed execution out of scope; jobd is that layer, plus GPU awareness
HyperQueueMulti-machine scheduling with HPC roots, single binaryCounts GPUs but doesn't track VRAM; no preemption/checkpoint contract or agent interface
SlurmDatacenter-grade schedulingHeavy to stand up and operate for 2–3 personal boxes
SkyPilot / dstackRuns on clouds and your own machinesSkyPilot's "existing machines" mode installs Kubernetes (k3s) on your boxes; dstack wants Docker and passwordless sudo on every host
ModalServerless GPU computeRuns on Modal's machines, not yours
RayDistributed compute; Ray Jobs runs any shell command on a Ray clusterYou first stand up and run a Ray cluster

Install

bash
pip install jobd               # broker + CLIpip install "jobd[mcp]"        # adds the MCP serverpip install "jobd[worker]"     # adds the worker daemon (jobd-worker)

Requires Python ≥ 3.11. scripts/install-worker.sh sets up a worker under ~/jobd-worker with its own venv and a generated config.

Quickstart (single host)

bash
# 1. start the broker (binds 127.0.0.1:8765 by default)JOBD_ALLOW_NO_AUTH=1 jobd          # no-auth is fine for a loopback-only broker
# 2. in another shell, install + start a worker pointed at itpip install "jobd[worker]"JOBD_URL=http://127.0.0.1:8765 JOBD_WORKER_HOST=local jobd-worker
# 3. submit a job and wait for itjob submit --project demo --wait -- echo hellojob listjob logs <id>

For a multi-host deployment (Docker broker, systemd workers, Tailscale binding, shared auth token), see docs/security.md and the templates in docker-compose.yml and scripts/. Adding a worker to a running fleet is one command:

bash
job fleet add user@newbox      # ssh in, install pinned to the broker's version,                               # wire systemd units + the self-update timer,                               # verify it registers. `job fleet status` shows drift.

Day-2 operations (health, draining a worker, upgrades, token rotation, backups) are in docs/runbook.md.

How it works

[Architecture: job CLI, jobd-mcp MCP tools, and HTTP/SSE clients talk to the jobd broker (FastAPI — queue, VRAM matcher, priorities, SQLite) over one tailnet; the broker dispatches via long-poll claims and heartbeats to workers A (24 GB GPU), B (8 GB GPU), and C (CPU-only)]

The broker (FastAPI + SQLite) holds the queue, runs the matcher and resolves per-project priorities. Workers, one per host, poll it, so nothing connects in to a worker: each heartbeats its live capacity, claims jobs it can run, streams logs back and honors preemption. The argv you submit runs as-is (shell=False); job submit --stdin and the MCP jobd_submit tool are the exceptions and run a command string as bash -c <string>. Design notes: docs/ARCHITECTURE.md.

CLI

job submit -p PROJ [options] -- CMD...       # queue a jobjob list [--state STATE] [--project P]       # queue + recent jobsjob status ID | A<id> [--watch]              # one job, or an array's aggregatejob logs ID [-n BYTES]                       # tail captured outputjob wait ID                                  # block until terminaljob cancel ID  /  job preempt ID             # stop a jobjob adopt --pid N -p PROJ [--gpu]            # track a process started outside jobd (Linux)job workers                                  # fleet snapshot + healthjob projects list | set NAME PRI | nudge NAME DELTAjob audit [--project P] [--since 24h]        # event history

Job arrays and sweeps, --explain dry runs, job adopt, and a pueue/task-spooler command map: docs/cli.md.

MCP / agent integration

jobd-mcp exposes the queue as nine tools: jobd_submit, jobd_status, jobd_logs, jobd_list, jobd_cancel, jobd_preempt, jobd_events, jobd_workers, jobd_worker_delete. docs/agent-cookbook.md walks through babysitting, preemption, sweeps, and asking why a job won't schedule.

For Claude Code:

bash
claude mcp add jobd \  --env JOBD_URL=http://127.0.0.1:8765 \  --env JOBD_API_TOKEN=<your-token> \  -- jobd-mcp

Any other MCP client:

json
{  "mcpServers": {    "jobd": {      "command": "jobd-mcp",      "env": {        "JOBD_URL": "http://127.0.0.1:8765",        "JOBD_API_TOKEN": "<your-token>"      }    }  }}

JOBD_API_TOKEN must match the broker's token, or every call returns 401. examples/claude-code-hooks/ has optional hooks that nudge (or hard-block) an agent toward submitting heavy commands through jobd.

What the benchmark shows

Claude with the server was compared with Claude using jobd's own job CLI, on 38 questions about a live broker, 3 runs each. With Claude Haiku 5.5 the server got all 38 right in every run and the CLI 29: a pre-registered win (Holm-adjusted McNemar p = 0.008). With Sonnet 5.5 it was 36 against 30 and with Opus 5.5 38 against 37, neither separated. On all three models the server's answers were cheaper and faster; with Sonnet, 0.20 times the cost and 0.17 times the time.

[Bar chart of right and wrong answers to 38 questions about a live jobd broker, 3 runs per question for each Claude setup. Constant guess with no model: 3 right, 35 wrong. Best constant fit to the answer key: 9 right, 29 wrong. Claude with the job CLI: 113 right, 1 wrong. Claude with jobd-mcp: 114 of 114 right.]

The chart is the Opus 5.5 run (2026-10-18). There the server's median answer cost 0.20 times as much (95% CI 0.15 to 0.28) and took 0.16 times as long (0.13 to 0.20), and it was cheaper and faster on all 38 questions. These were secondary endpoints, measured on an unreleased commit that adds a finished_at window to jobd_list. On an earlier, easier set both setups got 123 of 123.

We wrote the questions and ran the benchmark ourselves; an outside re-run is welcome. Method, controls and limitations: benchmarks/agent_qa; the Haiku and Sonnet runs: 2026-10-20 re-run.

Supported platforms

ComponentLinuxmacOSWindows
Broker (jobd)testedexpectedexpected (WSL recommended)
CLI (job) / MCP (jobd-mcp)testedexpectedexpected
Worker (jobd-worker)testeddegradeduntested

CI runs on Ubuntu with no GPU runner (VRAM paths are tested against mocks), and without a systemd user instance the worker still runs jobs but drops its memory caps, process reaping and preemption guarantees.

Configuration

Security

Run the broker on loopback or a Tailscale tailnet, never a public interface. Three stacked controls:

  1. Bind: JOBD_HOST is 127.0.0.1 by default; set it to a Tailscale address (100.64.0.0/10), never 0.0.0.0. The broker does not enforce this.
  2. Source IP: requests from outside loopback and 100.64.0.0/10 get 403 (JOBD_DISABLE_TAILNET_ACL=1 turns this off).
  3. Token: every host sets JOBD_API_TOKEN; JOBD_ALLOW_NO_AUTH=1 is for a loopback-only broker, and on any other bind exposes unauthenticated code execution to your tailnet.

/livez, /readyz and /metrics are exempt from both the source-IP check and the token; /metrics shows every worker's hostname and version, so don't port-forward the broker. Threat model and token rotation: docs/security.md.

License

MIT; see LICENSE.

Source: README.md at commit 11e0fdd

Tools

0
SourceWeft has not started this package in its sandbox yet, so its tools are not known.

Version history

8
  1. v0.5.52LatestOct 20, 2026
  2. v0.5.51Sep 26, 2026
  3. v0.5.50Sep 26, 2026
  4. v0.5.49Sep 24, 2026
  5. v0.5.48Sep 23, 2026
  6. v0.5.47Sep 23, 2026
  7. v0.5.46Sep 19, 2026
  8. v0.5.44Sep 16, 2026

Score

50% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts

    Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.

  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts