berth

T-CryptApache-2.0活跃维护

用 Go 写的轻量级 GPU 资源管理器,控制哪些应用能占用 GPU、何时占用以及如何交接,适合本地跑多个 LLM 推理服务时排队调度。

Berth is a lightweight GPU resource manager written in Go. It controls which applications get access to your GPU, when they get it, and how that access is handed off.

v0.5.0发布于 2026-11-08 16:06 UTC291star +2 / 7 天31fork同步于 2026-11-10 12:02 UTC

怎么上手

由 README 提炼,执行前请确认
go install github.com/T-Crypt/berth/cmd/berth@latest
berth service install --harbor
berth status
硬件要求按 README 说明

README 未给出具体显存要求;支持 Linux + NVIDIA(正式支持),Windows 10/11 + NVIDIA 为实验性支持,示例运行在 RTX 4090 工作站上。

支持 ROCm / AMD有 Docker 镜像
下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

用租约让多个 GPU 程序轮流使用同一张显卡,避免显存冲突和 OOM 崩溃

用 Go 写的轻量级 GPU 资源管理器,控制哪些应用能占用 GPU、何时占用以及如何交接,适合本地跑多个 LLM 推理服务时排队调度。

核心能力

  • 通过 berth run 让引擎排队租用显卡,等持有者结束回复后再卸载
  • 按 hold、foreground、interactive、batch 四级优先级排队并说明等待原因
  • 在 ComfyUI 任务边界完成交接,等当前图像任务跑完再切换
  • 支持把引擎权重暂存到内存,或用 cuda-checkpoint 实现快速切换
  • 检测未持租约却占用显存的进程,报告其 PID 和占用大小

容易踩的坑

项目处于 alpha 阶段(v0.5.0),功能可能不完整;Windows 支持仅为实验性;harbor 默认监听 127.0.0.1:9798,改端口需编辑 berth.toml 并重启服务。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.5.0
  1. v0.5.0Berth v0.5.0最新正式版2026-11-08 · 2026-11-08 16:06 UTC

    v0.5.0 puts the switchboard on a phone and lets one node send a request to another. The harbor's SWITCH tab switches a card to an engine, stops its holder and loads one llama-swap model from a paired device. A [[peer]] entry names another node's gateway on the tailnet, and a model no engine here serves goes to the first peer that lists it. berth batch keeps the card on one engine for a run of jobs, a lease window keeps work to the hours you set, a model split unevenly over two cards reserves a size per card, and park admission keeps room for the RAM a render will take back.

    Added

    • berth batch ENGINE and POST /v1/berth/batch: keep the card on one engine for a run of jobs. While the batch lasts, no waiter but a hold drains the engine, and the engine takes the card the way a switch does when it is the one waiting. The batch ends by itself once the engine has had no job running or queued for --grace (30 s), or after --max (4 h). An agent that queues forty images on ComfyUI no longer gives the card to its own chat model between images, and one image still goes back and forth as before. The ComfyUI skill tells agents when to use it.

    • On Linux, a parked engine is first in line for the OOM killer: its processes get oom_score_adj 1000 while parked, and their own scores back on resume. When RAM runs out anyway, the kernel or earlyoom kills the parked copy and not the render on the card.

    • [[peer]] in berth.toml: another node's gateway on the tailnet, with its key in an owner-private file. A request for a model no engine on this node serves goes to the first peer that lists it, and GET /v1/models lists the peers' models under their names. Each node's berthd still leases only its own cards. The peer URL must be a *.ts.net name, a tailnet address or loopback. docs/gateway.md has the section "Models on another machine".

    • Park admission keeps room for the RAM the card's holder and its waiters will take back. Berth reads each running engine's peak RAM from its scope and remembers the most any run of that engine held, so a ComfyUI render between stages, or a render waiting for the card, no longer lets a 30 GiB engine park into RAM the render needs a minute later. The refusal names the engine the RAM is kept for.

    • berth unpark ENGINE and berth unpark --all: drop a parked engine from RAM now instead of at its ttl, for a job that needs the machine's RAM. Only parked engines are touched; nothing parked is no error. berth park off|on|status switches RAM parking off for every card (evicting what is parked) and back on; berth status carries park_off while it is off. The switch lives in the daemon, so a restart starts with parking on. docs/gateway.md has the section.

    • needs_mib = [18000, 6000] on an [[engine]] entry with gpu = "gpu0,gpu1", and berth run --needs-mib 18000,6000: a size per card, in the order the cards are named, for a model split unevenly across cards (llama.cpp --tensor-split). Each card's share of the lease reserves its own size, so the room left on the lighter card can serve another engine. One value still sizes every card. A berthd from before this release reads only the largest size and reserves it on every card. docs/leasing.md has a new section on splitting one model across cards.

    • window = "23:00-07:00" on an [[engine]] entry, and berth run --window 23:00-07:00: a lease window, for work that should only run in cheap hours. berthd grants the lease inside them and drains its holder when they close, at the same boundary as any other drain. A run that asks outside its window, on one card or several, waits for it with no deadline unless --wait sets one, holds up no waiter behind it, and berth status says when the window opens. A gateway background engine is not started outside its window at all. A window needs a lease that can be asked back, so it goes with neither --pinned nor --priority hold.

    • `berth gateway

  2. v0.5.0-rc.1Berth v0.5.0-rc.1pre-release2026-11-08 · 2026-11-08 15:48 UTC
    查看更新说明

    A pre-release for testing. Production installs stay on the latest release; berth update never offers a pre-release.

    Added

    • berth batch ENGINE and POST /v1/berth/batch: keep the card on one engine for a run of jobs. While the batch lasts, no waiter but a hold drains the engine, and the engine takes the card the way a switch does when it is the one waiting. The batch ends by itself once the engine has had no job running or queued for --grace (30 s), or after --max (4 h). An agent that queues forty images on ComfyUI no longer gives the card to its own chat model between images, and one image still goes back and forth as before. The ComfyUI skill tells agents when to use it.

    • On Linux, a parked engine is first in line for the OOM killer: its processes get oom_score_adj 1000 while parked, and their own scores back on resume. When RAM runs out anyway, the kernel or earlyoom kills the parked copy and not the render on the card.

    • [[peer]] in berth.toml: another node's gateway on the tailnet, with its key in an owner-private file. A request for a model no engine on this node serves goes to the first peer that lists it, and GET /v1/models lists the peers' models under their names. Each node's berthd still leases only its own cards. The peer URL must be a *.ts.net name, a tailnet address or loopback. docs/gateway.md has the section "Models on another machine".

    • Park admission keeps room for the RAM the card's holder and its waiters will take back. Berth reads each running engine's peak RAM from its scope and remembers the most any run of that engine held, so a ComfyUI render between stages, or a render waiting for the card, no longer lets a 30 GiB engine park into RAM the render needs a minute later. The refusal names the engine the RAM is kept for.

    • berth unpark ENGINE and berth unpark --all: drop a parked engine from RAM now instead of at its ttl, for a job that needs the machine's RAM. Only parked engines are touched; nothing parked is no error. berth park off|on|status switches RAM parking off for every card (evicting what is parked) and back on; berth status carries park_off while it is off. The switch lives in the daemon, so a restart starts with parking on. docs/gateway.md has the section.

    • needs_mib = [18000, 6000] on an [[engine]] entry with gpu = "gpu0,gpu1", and berth run --needs-mib 18000,6000: a size per card, in the order the cards are named, for a model split unevenly across cards (llama.cpp --tensor-split). Each card's share of the lease reserves its own size, so the room left on the lighter card can serve another engine. One value still sizes every card. A berthd from before this release reads only the largest size and reserves it on every card. docs/leasing.md has a new section on splitting one model across cards.

    • window = "23:00-07:00" on an [[engine]] entry, and berth run --window 23:00-07:00: a lease window, for work that should only run in cheap hours. berthd grants the lease inside them and drains its holder when they close, at the same boundary as any other drain. A run that asks outside its window, on one card or several, waits for it with no deadline unless --wait sets one, holds up no waiter behind it, and berth status says when the window opens. A gateway background engine is not started outside its window at all. A window needs a lease that can be asked back, so it goes with neither --pinned nor --priority hold.

    • berth gateway --harbor URL redirects the gateway's /gui/ index and / to URL/#switch, so an old bookmark of the engine list lands on the harbor's SWITCH tab. The per-engine wake pages, their state endpoints and the wake POST keep answering, so a bookmark of one engine's web UI still opens its wake page. Without the flag, /gui/ is the index page.

    • The harbor's SWITCH tab loads one llama-swap model: a paired device taps LOAD on a model that is not loaded, and the

  3. v0.4.1Berth v0.4.12026-11-01 · 2026-11-01 17:35 UTC
    查看更新说明

    Each card on a machine with several now has its own gateway URL, /cards/CARD/v1, and one model can be served on more than one card. A plain /v1 request goes to the card that costs least to reach. berth cordon gpu1 takes a card out of rotation while the others serve, and the gateway's /gui/ page names each engine's card. berth hold --gpu, berth vfio run --gpu and gpu = ... on a [[claim]] hold only the cards they name. An engine can run under a systemd slice, the harbor shows the claims that match now, and an engine Berth stopped no longer shows as unresponsive.

    Added

    • GET /v1/berth/board names the cards each engine's entry runs on (cards), and each card's base URL on the gateway (base) and whether it is cordoned. On a node with several cards, the gateway's /gui/ page names each engine's card, so launching the copy of a model on gpu1 picks that card from a phone.
    • berth cordon CARD stops new grants on a card, holds aside, while the node's other cards serve; berth uncordon CARD lifts it. What holds the card keeps running. Waiters say the card is cordoned, berth status marks it CORDONED, and the gateway treats it as out of reach: no background engine starts there and a ComfyUI job for it queues. A berthd restart clears the cordon.
    • Each card has its own base URL on the gateway, /cards/CARD/v1, named by the card's name or PCI address. A request through it only reaches engines on that card, and GET /cards/CARD/v1/models lists only theirs. One model can now be served on several cards, one engine entry per card, and berth config check still refuses two entries that serve it on one card.
    • A plain /v1 request for a model served on several cards goes to the card that costs least to reach: the engine that is up, then one parked in RAM, then a free card, then a card whose holder drains, and last a card a VM or a pinned lease keeps. Cards of the same rank go to the first entry in berth.toml.
    • gpu = "gpu1" on a [[claim]] holds only the cards it names, as berth run --gpu names them, on a node with several cards. Engines on the other cards keep running, and the gateway starts background engines there. A claim without gpu still holds every card. berth config check refuses a bad card name and a gpu on a track claim. A name the node does not have is logged when berthd starts, and that claim holds every card.
    • berth hold --gpu CARD and berth vfio run --gpu CARD name the card on a node with several. hold takes a comma list for one hold on several cards, and vfio run takes one card. Before, both always took the node's first card. Without --gpu they still do.
    • slice = "NAME.slice" on an engine entry, or berth run --slice NAME.slice (which wins), starts the engine's transient scope under that systemd slice on a Linux user manager: the slice unit's resource policy, CPU, memory and swap limits, applies to the engine and everything it forks, not only to the wrapper that asked for the card. berth config check refuses a slice name no unit can take. An entry with no slice starts its scope exactly as before, on every platform.
    • The harbor's card tab has a CLAIMS panel: the claim rules matching now, as berth claims lists them. A hold names its lease or says it is waiting, a track gives the card memory it names, and on a machine with several cards each row names its card. Before, a tracked app such as OBS did not show in the harbor at all.

    Changed

    • The harbor's About tab is rewritten for v0.4. It leads with the node's version and links to the docs, changelog, issues and source, adds a section on machines with several cards, and drops the list of planned features that had shipped or changed. Its sections are cards in two columns on a wide screen, with the same section rail as the other tabs.
    • The harbor header no longer clips the ADVANCED switch or cuts its tagline off: below 1600 px the tagline and the "& REQUESTS"
  4. v0.4.1-rc.1Berth v0.4.1-rc.1pre-release2026-11-01 · 2026-11-01 16:08 UTC
    查看更新说明

    A pre-release for testing. Production installs stay on the latest release; berth update never offers a pre-release.

    Added

    • GET /v1/berth/board names the cards each engine's entry runs on (cards), and each card's base URL on the gateway (base) and whether it is cordoned. On a node with several cards, the gateway's /gui/ page names each engine's card, so launching the copy of a model on gpu1 picks that card from a phone.
    • berth cordon CARD stops new grants on a card, holds aside, while the node's other cards serve; berth uncordon CARD lifts it. What holds the card keeps running. Waiters say the card is cordoned, berth status marks it CORDONED, and the gateway treats it as out of reach: no background engine starts there and a ComfyUI job for it queues. A berthd restart clears the cordon.
    • Each card has its own base URL on the gateway, /cards/CARD/v1, named by the card's name or PCI address. A request through it only reaches engines on that card, and GET /cards/CARD/v1/models lists only theirs. One model can now be served on several cards, one engine entry per card, and berth config check still refuses two entries that serve it on one card.
    • A plain /v1 request for a model served on several cards goes to the card that costs least to reach: the engine that is up, then one parked in RAM, then a free card, then a card whose holder drains, and last a card a VM or a pinned lease keeps. Cards of the same rank go to the first entry in berth.toml.
    • gpu = "gpu1" on a [[claim]] holds only the cards it names, as berth run --gpu names them, on a node with several cards. Engines on the other cards keep running, and the gateway starts background engines there. A claim without gpu still holds every card. berth config check refuses a bad card name and a gpu on a track claim. A name the node does not have is logged when berthd starts, and that claim holds every card.
    • berth hold --gpu CARD and berth vfio run --gpu CARD name the card on a node with several. hold takes a comma list for one hold on several cards, and vfio run takes one card. Before, both always took the node's first card. Without --gpu they still do.
    • slice = "NAME.slice" on an engine entry, or berth run --slice NAME.slice (which wins), starts the engine's transient scope under that systemd slice on a Linux user manager: the slice unit's resource policy, CPU, memory and swap limits, applies to the engine and everything it forks, not only to the wrapper that asked for the card. berth config check refuses a slice name no unit can take. An entry with no slice starts its scope exactly as before, on every platform.
    • The harbor's card tab has a CLAIMS panel: the claim rules matching now, as berth claims lists them. A hold names its lease or says it is waiting, a track gives the card memory it names, and on a machine with several cards each row names its card. Before, a tracked app such as OBS did not show in the harbor at all.

    Changed

    • The harbor's About tab is rewritten for v0.4. It leads with the node's version and links to the docs, changelog, issues and source, adds a section on machines with several cards, and drops the list of planned features that had shipped or changed. Its sections are cards in two columns on a wide screen, with the same section rail as the other tabs.
    • The harbor header no longer clips the ADVANCED switch or cuts its tagline off: below 1600 px the tagline and the "& REQUESTS" half of the engines tab are hidden. On a phone an alert's message wraps instead of ending in an ellipsis, with its time on its own line.

    Fixed

    • An engine that Berth stopped no longer shows as unresponsive (LATCHED) in berth status and the harbor. A drain, berth stop, a wake that failed or a stop signal from a supervisor such as llama-swap ends the engine on purpose, and it now shows as offline, as an engine that exits 0 already did. An engine t
  5. v0.4.0Berth v0.4.02026-10-29 · 2026-10-29 23:04 UTC
    查看更新说明

    One berthd now governs every card on a machine. berth run --gpu gpu1 puts an engine on one card, --gpu gpu0,gpu1 holds one lease on several, and berth run --pinned keeps a training run's cards. The gateway, berth status, berth notify, /metrics and the harbor name the card, and one card can go to a VM while the others serve. Engines with no offload call of their own, llama.cpp and NInfer included, park in RAM through cuda-checkpoint. whole_machine = true is for an engine such as Strata that needs the card, the RAM and the CPU, so nothing parks beside it. A Docker Compose project runs as one engine (#172). An engine that can shrink a cache can lend part of its card and keep serving (experimental, #189). berthd uses less CPU, and the AMD probe no longer stops berthd on a desktop session (#196).

    Added

    • The harbor names the model each lease serves on its card's panel: the model its engine reports loaded or ready, or the engine name when it reports none. A lease on several cards shows on each with its card list (on gpu0..gpu4 when the cards are consecutive, else on gpu0,gpu2). A card with one lease reads as before plus the model name.

    • whole_machine = true on an engine entry is for engines that need the card, the RAM, the CPU and the disk to themselves, such as Strata. While one waits, berthd stops the engine it drains instead of parking it, and evicts any parked engine before the grant. A Strata started beside a parked ComfyUI lost the 0.5 GiB its CUDA context kept on the card, and one started beside a checkpoint-parked NInfer found 20 GiB of RAM free for its 40 GiB of experts.

    • /metrics is per card. GPU memory series gain a card label, and each card reports its baseline, utilization, temperature, power, power limit and fan speed when the driver gives them. Leases, the queue and stowaways count per card, berth_lease_used_bytes shows what each engine holds, and berth_build_info carries the version.

    • docs/leasing.md has a section on training runs. One berth run --pinned lease across several cards keeps all of them from a foreground run, a game's hold and another multi-card lease, and it survives a berthd restart. The --pinned help no longer says it is only for apps.

    • On a Proxmox VE host, a GPU process inside an LXC container shows as a stowaway named by its container, lxc:101 (ollama), where it showed as a bare PID. Berth reads the VMID from the process's cgroup (/lxc/101/...) and the name from the hostname in /etc/pve/lxc/101.conf. That file is readable by root and www-data only, so a berthd running as a normal user shows lxc:101 alone. LXC's lxc-start monitor is not a container, and Docker inside an LXC still resolves to the Docker container (#128).

    • berth wire llama-swap --wait DUR writes --wait on every model's line, so a batch request can queue behind a long foreground lease instead of giving up after berthd's default 120 s (#231).

    • berth run --as NAME with no command runs the start of NAME's entry in berth.toml, as the gateway does, so a configured engine no longer needs its command typed again. An entry matched only through --group never lends its start, because that is the group's engine (llama-swap), not this one. azrael-229 asked for it (#232).

    • [squatters] allow in berth.toml lists process names that are never squatters, as berth daemon --allow does. A unit or scheduled task from berth service install had no way to pass --allow, so on a desktop a browser or a terminal kept the card from ever being free and background engines never started. The flag still replaces the list when given (#172).

    • One lease across several cards: berth run --gpu gpu0,gpu1, or gpu = "gpu0,gpu1" on an engine entry. berthd grants the lease on every named card at once, only whe

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    Berth

    Berth makes the programs on your PC take turns with the GPU's memory, so they stop crashing each other, and tells you who is holding it.

    Berth decides who gets your GPU. Chat models, image pipelines, voice engines, games and GPU apps lease the card instead of grabbing it, so two of them never load at once and crash each other with an out-of-memory error. One Go binary, no cloud account, nothing else to install.

    berth daemon &                                        # one broker for every card
    berth run --as llama -- llama-server -m model.gguf    # this engine now takes turns
    berth status                                          # who holds the card, who waits, and why
    

    How it works

    Every engine asks berthd for the card: through berth run, the gateway, ComfyUI's API or a game claim. When another engine holds it, Berth:

    1. queues the request by priority: hold, foreground, interactive, batch;
    2. lets the holder finish its reply or image job, then unloads it;
    3. waits until the driver shows the memory free;
    4. grants the lease and starts the next engine, and the request that waited streams.

    The numbered markers on the timeline are those four steps. A process that holds VRAM without a lease shows up as a stowaway: Berth reports its PID and memory and leaves it running.

    Status: alpha, v0.5.0. Runs daily on an RTX 4090 workstation beside llama-swap, NInfer, Strata, ComfyUI and games, and testers run it on Windows. The docs cover what works and has tests; the roadmap lists the phases and what is still missing.

    Berth and llama-swap

    Use both. llama-swap swaps the models in its own config, and does it well. Berth governs the whole card: llama-swap's models, ComfyUI, Ollama, vLLM, games, and any process that holds VRAM, including the ones neither tool started. With Berth beside it, llama-swap's next model waits for ComfyUI or the engine before it to give the memory back, and when a crashed process still holds part of the card, Berth names its PID and how much it holds, so you aren't hunting through nvidia-smi while a load fails. berth wire llama-swap adds the lease to every model in your config in one pass, and --update rewrites its own flags on a later run while it keeps the ones you added by hand. An upstream integration with llama-swap is planned, so the wrapper line goes away.

    What Berth does

    Shares one card between everything

    • Engines take turns. You start each one through berth run. When another needs the card, Berth waits for the holder's current reply to end, unloads it (its own unload, then SIGTERM, then SIGKILL) and lets the next one in only after the driver shows the memory free.
    • Priorities. foreground, interactive, batch, and hold above them all. A lower request waits and gets a reason naming the holder, never an OOM crash.
    • No ping-pong. When two clients ask for different engines at once, a ranking picks one and the other waits, so the card stays put.
    • ComfyUI at a job boundary. Berth waits for the running image job to finish before the handoff.
    • A minimum turn. min_turn = "5m" keeps an engine on the card for five minutes after it gets it, whatever asks next; only a hold takes it out sooner.
    • Engines in containers. berth run --container NAME -- docker run ... puts a Docker or Podman engine under its lease, and the drain stops it through docker stop. An engine that is a Compose project names its Compose file instead (--compose PATH), and the lease then covers every container of the project.
    • Survives restarts. Upgrade or restart the daemon and your running engines stay up and re-attach. The gateway's engines survive it too: it starts each one as its own berth run process (in its own systemd scope on Linux) and adopts them again on its next start.

    Faster switches, and a way out

    • Park in RAM. An engine that can offload itself (ComfyUI, vLLM, SGLang, or any engine that serves the park protocol) moves its weights to system memory on a drain and resumes from there instead of reloading from disk. Berth parks only when the RAM it measures covers the engine plus a floor, and stops parked engines first when memory runs short.
    • Park any engine with cuda-checkpoint. An engine with no offload call of its own, such as llama.cpp or NInfer, parks through NVIDIA's cuda-checkpoint behind berth run --park checkpoint --front. A 23.1 GB NInfer comes back in 3.3 s where a reload takes 14 s. See Park in RAM.
    • Engines that need the whole machine. whole_machine = true on an engine entry, such as Strata's, makes berthd stop the engine it drains and evict any parked engine before the grant, so nothing holds a slice of the card or the RAM beside it.
    • Lend part of a card (experimental). An engine that can shrink a cache gives memory to a sized waiter and keeps serving, instead of being drained. See Lending part of a card.
    • A way out of every lease state. berth stop ENGINE drains an engine on purpose, berth resume ENGINE puts a parked one back, and berth reclaim stops what a stuck release left behind, once you confirm. berth stop also ends a parked engine, checkpointed ones included.
    • A place in the queue. Every wait reason starts with the waiter's place ("2nd in line (behind interactive comfyui)"), and berth status adds a start estimate from measured switches.

    Games and other apps

    • Games take the card while you play. Launch a Steam, Lutris or gamescope game on Linux, or a Steam, Epic, GOG, EA, Ubisoft, Xbox or Battle.net game on Windows, and Berth drains your AI engines until you quit.
    • Creative apps keep their memory. Berth names what Blender, DaVinci Resolve, OBS, Kdenlive, Premiere and After Effects hold and counts it against the card, without a stowaway report.
    • Your own rules. [[claim]] in berth.toml covers anything else: a training run, a render farm job, a cracking session, by name, command line or launcher.
    • berth hold frees the card for a call or a game you start yourself. Ctrl-C gives it back. On a node with several cards, --gpu names the ones it takes.
    • berth run --pinned for an app or a training run that must never be stopped mid-work, on one card or several. See Training runs.

    One API for your engines (preview)

    • berth gateway: one OpenAI-compatible port. Name a model and Berth starts its engine, unloads whatever holds the card, waits for the load and streams the answer.
    • It proxies ComfyUI's workflow API too, so an image request queues behind the same lease.
    • Wake pages (--gui-listen): a bookmark to a stopped engine's web UI shows who holds the card and a Launch button, says what the start waits for, and opens the UI once it answers.
    • Bearer key on every API route, loopback only, browser origins refused.

    Models

    • berth pull hf:org/repo:Q4_K_M downloads from Hugging Face, resumes where it stopped, and checks every file against the size and sha256 the Hub publishes before using it.
    • berth pull civitai:VERSION_ID does the same from CivitAI, with the hash the site publishes, and berth models shelves what it finds as chat, image or audio so a machine reading a shared root knows what a folder is.
    • berth storage shows each model root's filesystem, free space against its floor, and the other operating systems it is shared with.
    • berth verify re-checks pulled models offline and names any file that changed.
    • berth models lists every model on the machine (GGUF, safetensors, the Hugging Face cache, LM Studio and Ollama's stores) with quant, size, parameters, context and when each was last used, so you can see what earns its disk space.
    • berth models export ollama turns a GGUF you already have into an Ollama model.

    Keeps itself current

    • berth update asks the release page whether a newer berth exists and, when one does, downloads this machine's binary and the release's SHA256SUMS, checks the hash, and replaces the running binary in place after you confirm. It then names what to restart; it never restarts anything itself.
    • Update check. The harbor asks GitHub at most once every 24 h, one GET to the API's releases/latest, cached in the state directory, and shows "Update available" in its header when the answer is a newer release. [update] check = false in berth.toml turns the check off, and with it off Berth never contacts GitHub on its own. berth update and berth update --check ask GitHub every time you type them and never read the harbor's cached answer.

    Sees everything on the card

    • Finds your engines. Berth reads llama.cpp, llama-swap, Ollama, vLLM, SGLang, ComfyUI, Kokoro, LM Studio, KoboldCpp, TabbyAPI, LocalAI and more on their default ports, plus any OpenAI-compatible or custom server you name.
    • Stowaways. Berth reports any process holding VRAM without a lease, with its PID, name and memory, and leaves it running.
    • Built-in telemetry. Per-card memory, load, temperature, power and clocks; tokens per second, queue, latency percentiles, KV cache use, a token ledger and a per-model leaderboard; 30 days of history on disk.
    • The harbor web UI (berth harbor): a departures board of leases, a collisions-avoided counter, charts and the event log. Read-only, loopback by default.
    • Alerts to the desktop or a webhook, /metrics for Prometheus, and berth export for CSV or JSON.

    Built for agents and scripts

    • berth skill install teaches Claude Code, Codex, OpenCode, Hermes Agent and other agents to ask Berth for the card instead of killing GPU processes, and to set up and run ComfyUI image generation through Berth.
    • Every reporting command takes --format json|yaml. All of them keep results on stdout and diagnostics on stderr, and exit 0, 1 or 2 by one rule.

    Runs on Linux with an NVIDIA GPU (supported), Linux with AMD RDNA3/RDNA4 discrete GPUs or a Phoenix APU such as the 780M (experimental), and Windows 10/11 with an NVIDIA GPU (experimental). Apache-2.0.

    AMD on Linux

    Select the AMD probe explicitly (or let auto select it on an AMD-only host):

    berth daemon --gpu-backend amd --gpu 0
    # Or install the user service with that selection:
    berth service install --harbor --gpu-backend amd --gpu 0
    

    For AMD, --gpu N means Linux DRM cardN, not a HIP/ROCm ordinal. The probe matches processes by PCI address, not PID alone. When nvidia-smi is installed, auto keeps the NVIDIA probe, so on a machine with both vendors pass --gpu-backend amd to govern the AMD card. This selects what Berth observes, not where the engine loads: configure the engine's GPU placement separately.

    The AMD adapter reads kernel sysfs and DRM fdinfo; no ROCm installation, amd-smi, root privileges, or extra monitoring service is required. Phoenix APUs keep reserved UMA VRAM and shared-RAM GTT separate; use exclusive leases (omit --needs-mib). New APU admissions require host RAM above --headroom-mib (default 2048 MiB); parking is not yet supported on APUs, so engines use the normal drain. Other APU generations and AMD Windows are not supported. See scope and validation.

    Multiple GPUs (experimental)

    One berthd governs every card it finds. Each card is named gpu plus its index and keeps its own leases and queue, so a foreground request on gpu0 never drains a holder on gpu1. berth status lists every card and names each lease's card, and a GPU process without a lease shows as a stowaway on its card. To govern only some cards, give --gpu a comma list of indices and NVIDIA PCI addresses (--gpu 0,0000:41:00.0), or --gpu 0 for the first card alone, as before v0.4. A service unit installed earlier keeps the --gpu it was written with. Name an NVIDIA card by its PCI address when it can be passed through to a VM, so berthd keeps the right card when nvidia-smi renumbers them. A berthd named by address starts while its card is in a VM and takes it over when it comes back. An AMD card is named by its DRM cardN number (--gpu 1 for card1). --gpu does not take an AMD card's PCI address yet.

    An engine names its card with berth run --gpu gpu1, or gpu = "gpu1" on its [[engine]] entry. The lease goes to that card, and the engine starts with CUDA_VISIBLE_DEVICES set to the card's UUID, so it never opens another card. An environment that already sets CUDA_VISIBLE_DEVICES keeps its value. A run that names no card goes to the first card and its environment is left alone. A container engine picks its card itself, for example with docker run --gpus device=<UUID> (nvidia-smi -L lists the UUIDs).

    berth run --as maya --gpu gpu1 -- llama-server -m maya.gguf --port 8091
    

    An engine that spans several cards names them all: berth run --gpu gpu0,gpu1, or gpu = "gpu0,gpu1". It holds one lease across those cards, with --needs-mib on each (one size for all, or one per card: --needs-mib 18000,6000). The lease is granted on every card at once, and only when each one fits, so no card sits reserved while another is awaited. CUDA_VISIBLE_DEVICES lists each card's UUID in the order named. A drain on any of the cards stops the engine, and its exit frees every card. A lease on several cards cannot park or lend yet: a drain stops it the usual way.

    The gateway judges each engine against the cards it runs on, and each card has its own base URL on the gateway, /cards/gpu1/v1, so a client can choose the card (One URL per card). A plain /v1 request for a model served on several cards goes to the cheapest card to reach: a parked copy, then a free card, then one whose holder drains.

    berth hold --gpu gpu1, berth vfio run --gpu gpu1 and gpu = "gpu1" on a [[claim]] take only the cards they name, so the other cards keep serving.

    Quick start

    go install github.com/T-Crypt/berth/cmd/berth@latest      # or a release binary, see Install
    berth service install --harbor                            # berthd and the harbor, as user services
    berth status                                              # the card, leases, the engines and their estimated VRAM
    berth run --as llama --priority foreground -- llama-server -m model.gguf --port 8080
    

    On Linux the services are systemd user units; on Windows they are Task Scheduler logon tasks that need no admin and run in your session, where your GPU apps are. The harbor answers on http://127.0.0.1:9798/. berth service status shows them and berth service uninstall removes them; the manual steps in Install do the same by hand.

    The harbor and gateway take their addresses from berth.toml when their --listen flags are not given: [harbor] listen and [gateway] listen, so a port change is a config edit and a restart of the service, not a reinstall. On Windows, installing also starts the tasks it creates, and each task appends its output to a log file under the state directory, which berth service status prints.

    Preview

    The first two screenshots show the harbor web UI with its built-in demo data, on the CARD tab and on the Engines tab in advanced mode. The third shows the CARD tab of a live node on an iPhone. The harbor and telemetry explains every panel and shows how to add the harbor to an iPhone's Home Screen.

    Install

    berth service install --harbor in the quick start sets up the daemon and the harbor as user services. To install from a release binary instead, check it against the release's SHA256SUMS:

    curl -fLO https://github.com/T-Crypt/berth/releases/latest/download/berth-linux-amd64
    curl -fLO https://github.com/T-Crypt/berth/releases/latest/download/SHA256SUMS
    sha256sum --check --ignore-missing SHA256SUMS
    install -Dm755 berth-linux-amd64 ~/.local/bin/berth
    

    Install has a prompt that hands the whole install to your coding agent, the manual steps, the configuration file and how to uninstall.

    Documentation

    The same pages are a site with search at https://t-crypt.github.io/berth/.

    PageWhat it covers
    InstallThe agent prompt, manual install, berth.toml, uninstall
    EnginesEvery engine Berth reads, API keys, ComfyUI as a suite
    Leasing, games and holdsPriorities, drains, stuck releases, the juggle guard, minimum turns, claims and berth hold
    DesignThe lease model parks, lends, many cards and many machines follow, the rules none may break, and the order they ship in
    VFIO passthrough (experimental, Linux)berth vfio status, berth vfio run, the IOMMU requirements, an example switch script, a box with two NVIDIA cards
    Modelsberth pull, integrity checks, shelves, berth storage, the page cache and berth prefetch
    GatewayOne OpenAI-compatible port, cold starts, fallbacks, KV snapshots, switch costs, wake pages, park in RAM
    Hermes AgentHermes on the gateway, auxiliary models, cold starts, the skill
    The harbor and telemetryStowaways, the collisions board, alerts, every harbor panel, what Berth measures
    InterfacesThe CLI's output contract and exit codes, the harbor's options, Prometheus
    BenchmarksTested hardware and measured swap times
    Testing a pre-releaseInstalling a vX.Y.Z-rc.N tag from the next branch, and going back to the release
    RoadmapPhases, what each release shipped, what comes next
    FAQCommon questions, Docker engines included

    Support matrix

    PlatformStatus
    Linux + NVIDIASupported
    Windows 10/11 + NVIDIAExperimental: leases, process attribution, job-object drains and game claims work, and the test suite runs on Windows in CI. Per-process VRAM comes from the GPU Process Memory counter (Task Manager's source), which also lists graphics apps and games nvidia-smi omits; host RAM and the page file come from GlobalMemoryStatusEx, and root space from GetDiskFreeSpaceEx. berth service install sets the node up as logon tasks; still no packaged installer and no locked-down control pipe. contrib/windows/check.ps1 compares Berth's per-process VRAM with Task Manager's and writes a report for the drain report form.
    AMD on LinuxExperimental: discrete RDNA3/RDNA4 and Phoenix APUs through amdgpu sysfs/DRM. W7900 and 780M bounded allocation/handoff validated; RDNA4 fixture-tested, hardware validation pending. APU leases are exclusive, with independent UMA/GTT accounting and host-RAM admission checks. Scope and checks.
    Intel, AppleNot supported yet
    Multiple GPUsExperimental: one berthd governs every card, and berth run --gpu puts an engine on one. One card can be passed through to a VM while the other keeps serving (two NVIDIA cards). One lease across several cards works with --gpu gpu0,gpu1.

    Tested hardware

    The table of cards Berth has run on, and how to add yours, is in Benchmarks.

    Swap numbers

    On the reference RTX 4090 with 62.5 GiB of RAM, one chat request through llama-swap, from the request to the reply, lease included (2026-10-23):

    SwitchTime
    A 21 GB chat model, file in the page cache5.6 s
    ComfyUI parks in RAMunder 1 s
    ComfyUI to a 27B chat model13.6 to 16.3 s

    Strata's numbers and the method are in Benchmarks.

    Roadmap

    The phases, the shipped releases and what comes next are in the roadmap.

    What Berth never does

    • Kill a process it did not start, or any process by default.
    • Grant a card because a process exited, before the driver shows the memory free.
    • Claim memory is reserved when it is only estimated to be free.
    • Take a card from another user's machine without their consent (on one machine, only the priorities you set decide).
    • Require a cloud account, telemetry upload, or any other software to work.

    Contributing

    Issues and pull requests are welcome. AGENTS.md has the one command that proves a change (build, format, vet, tests, lint, as CI runs them) and the invariants a change must keep.

    Star History

    License

    Apache License 2.0. Copyright 2026 Trevin Tindall (T-Crypt), who created Berth and maintains it here. Forks are welcome: keep the NOTICE file and the copyright header in each source file, mark the files you change, and release your fork under its own name. The Berth name, the Pilot mascot and the artwork in assets/ are not covered by the license. Third-party components: THIRD_PARTY_NOTICES.