All projects
Beta Homelab & Storage

Balancebot

See your array. Move your data. Never lose a byte.

An Unraid plugin that turns disk rebalancing from a scripted chore into a visual, drag-and-drop operation — a WizTree-style treemap of every array disk that doubles as a dependency-aware move planner and a safe, resumable copy → verify → delete engine.

Go (stdlib)React 18 + TypeScriptViteHTML CanvasrsyncUnraid plugin (.plg / .txz)
Balancebot all-disks overview — a treemap of every array disk, colored by share
The all-disks overview — every array disk as a squarified treemap, colored by share.

The problem

Rebalancing an Unraid array is high-stakes and low-visibility. The existing tooling is a checkbox tree over a log window — it can't show you where your space actually is, can't express migrations that require freeing space before filling it, and asks you to trust an opaque ordering with terabytes of irreplaceable data.

The approach

Make the visualization and the planner the same surface. A canvas treemap gives immediate spatial understanding of each disk; dragging on it builds a plan; a space-dependency simulator proves the plan fits and orders it; and a layered safety system makes execution trustworthy.

The map is the planner

Every array disk is rendered as a squarified treemap — think WizTree or WinDirStat — colored by share, zoomable from disk → share → folder → file. To plan a migration, you drag a folder block from one disk onto another. The source block goes hatched, a ghost block previews where the data will land, and every disk updates its projected free space after the plan in real time.

Behind the visual is a space-dependency simulator. Complex rebalances often require interleaving — you can't move data onto disk B until you've first freed space on it by moving its data to disk C. Balancebot topologically orders the queued moves so every step fits at the moment it executes, respecting a configurable per-disk free-space floor, and flags any step that won't ("step 4 needs 800 GB on disk2 but only 320 GB will be free").

Because this operates on terabytes of irreplaceable data, safety is the spine of the design. Every operation runs copy → verify → delete: the source is deleted only after the destination is verified, so a crash can only ever leave a harmless duplicate, never lost data. On any failure the queue halts with state preserved, and runs are resumable across daemon restarts and reboots.

Features

WizTree-style treemap, per disk

A fast, canvas-rendered, zoomable map of every array disk, colored by share — see where your space went instead of reading du output.

The map is the planner

Drag a folder from one disk onto another to queue a move. Queued data is hatched, incoming data ghosted, and free space is projected live.

Dependency-aware auto-ordering

A space simulator sequences moves so each one fits when it runs — enabling interleaved migrations a flat list can't express. Manual reorder re-validates live.

Verify-before-delete engine

rsync copy → checksum verify → delete, per operation. A crash leaves a duplicate, never a loss.

Halt-and-preserve, resume anywhere

Any error stops the queue with state intact; resume picks up the remaining steps, even after a reboot.

Pre-flight safety checks

Open-file detection (with process names), share include/exclude and split-level conflicts, permissions audit, parity-activity awareness — each graded and overridable.

Dry-run reports

See exactly what every plan would move, byte for byte, before a single file changes.

Scatter / Gather shortcuts

Spread one disk across many, or consolidate a share onto one — built on the same safe engine.

Native-feeling Unraid plugin

One embedded Go+React binary, array lifecycle integration, dark/light theming, flash-backed persistence.

In action

Zoomed into a share — every file sized to scale, WizTree-style.
Zoomed into a share — every file sized to scale, WizTree-style.
Drag a folder to another disk: source hatched, move queued, free space reprojected.
Drag a folder to another disk: source hatched, move queued, free space reprojected.
The planner: per-disk cards, projected free space, and a no-touch dry-run report.
The planner: per-disk cards, projected free space, and a no-touch dry-run report.
Live execution — copy → verify → delete, per-step progress, rate and ETA.
Live execution — copy → verify → delete, per-step progress, rate and ETA.
Run history with expandable per-step logs and one-click resume.
Run history with expandable per-step logs and one-click resume.
Themed to match the Unraid webGUI, light and dark.
Themed to match the Unraid webGUI, light and dark.

How it's built

Single-binary architecture

A Go daemon (balancebotd) serves a REST API and the entire React SPA, embedded at compile time with go:embed. One file deploys the whole app.

Stdlib-only Go backend

No web framework — Go 1.22+ pattern-based ServeMux routing and standard-library concurrency for the parallel disk walker and scan cache. Deliberately dependency-light for a plugin trusted with root file operations.

Space-dependency solver

The planner simulates free space on every disk after each step to topologically order operations, honoring a reserve floor of max(configurable, 1% of disk) — turning “does this migration even fit?” into a validated, visualized answer.

Canvas treemap from scratch

Squarified treemap layout, devicePixelRatio-aware, with hit-testing for hover and drag, queued-state hatching overlays, and ghost-block previews. No charting library.

Crash-safe execution model

Runs are journaled atomically to flash on step transitions (never on progress ticks, to spare the flash drive). Copy → verify → delete makes every step idempotent; interrupted runs are detected and revalidated before resume.

Pluggable mover with a pure-Go fallback

An rsync-backed mover for production and a dependency-free Go mover for dev/test, selected at runtime — so the whole engine is unit-tested (halt, resume, verify-catches-corruption) on any OS without rsync.

Real plugin packaging

.plg installer + Slackware .txz, rc-style start/stop wired to Unraid's array lifecycle events, PHP wrapper page for the webGUI iframe, config and state persisted under /boot/config.

Status

Feature-complete and fully unit-tested; real-hardware validation on a live Unraid array is in progress. The engine was developed and tested against sandboxed disks with a pure-Go mover — the rsync mover, open-file detection, and permission audits exercise for the first time on real hardware.