|
| 1 | +# TAGLINE |
| 2 | + |
| 3 | +single-binary homelab CLI that reports what changed on a server |
| 4 | + |
| 5 | +# TLDR |
| 6 | + |
| 7 | +**Interactive setup** (writes `~/.config/homebutler/config.yaml`) |
| 8 | + |
| 9 | +```homebutler init``` |
| 10 | + |
| 11 | +Current **CPU, memory, disk, and uptime** |
| 12 | + |
| 13 | +```homebutler status``` |
| 14 | + |
| 15 | +Butler-style **health report** plus what changed since the last snapshot |
| 16 | + |
| 17 | +```homebutler report``` |
| 18 | + |
| 19 | +Read-only **doctor** check (resources, stopped containers, public ports, backups, Proxmox) |
| 20 | + |
| 21 | +```homebutler doctor --strict``` |
| 22 | + |
| 23 | +Map **containers, ports, and topology** |
| 24 | + |
| 25 | +```homebutler inventory scan``` |
| 26 | + |
| 27 | +Show only **ports bound on all interfaces** |
| 28 | + |
| 29 | +```homebutler inventory scan --filter exposed``` |
| 30 | + |
| 31 | +**List** Docker containers |
| 32 | + |
| 33 | +```homebutler docker list``` |
| 34 | + |
| 35 | +Deploy a **self-hosted app** via generated Compose |
| 36 | + |
| 37 | +```homebutler install [uptime-kuma]``` |
| 38 | + |
| 39 | +Verify a **backup actually boots** in an isolated container |
| 40 | + |
| 41 | +```homebutler backup drill [uptime-kuma]``` |
| 42 | + |
| 43 | +Terminal **dashboard** for every configured server |
| 44 | + |
| 45 | +```homebutler watch tui``` |
| 46 | + |
| 47 | +Embedded **web dashboard** (default `127.0.0.1:8080`) |
| 48 | + |
| 49 | +```homebutler serve --token [secret]``` |
| 50 | + |
| 51 | +MCP server on **stdio** for AI agents |
| 52 | + |
| 53 | +```homebutler mcp``` |
| 54 | + |
| 55 | +Same commands as **JSON** for scripts |
| 56 | + |
| 57 | +```homebutler report --json``` |
| 58 | + |
| 59 | +# SYNOPSIS |
| 60 | + |
| 61 | +**homebutler** [**--json**] [**-v**] [**--config** _path_] [**--server** _name_ | **--all**] _command_ [_options_] |
| 62 | + |
| 63 | +# PARAMETERS |
| 64 | + |
| 65 | +**--json** |
| 66 | +> Force machine-readable JSON instead of terminal text. Persistent; accepted on most commands. Colour is dropped automatically when output is piped, redirected, or run from cron. |
| 67 | +
|
| 68 | +**-v**, **--verbose** |
| 69 | +> Show detailed error information. |
| 70 | +
|
| 71 | +**--config** _path_ |
| 72 | +> Config file. Resolution order is this flag, then **$HOMEBUTLER_CONFIG**, then **~/.config/homebutler/config.yaml**, then **./homebutler.yaml**. If none exist, built-in defaults are used. A **--config** path that does not exist also falls back to defaults rather than failing (except **config validate**). |
| 73 | +
|
| 74 | +**--server** _name_ |
| 75 | +> Run the command on a named remote server from the config over SSH (skipped when that server is marked **local**). Not supported by **proxmox** (use **--endpoint** instead). **deploy** and **upgrade** handle remotes themselves. |
| 76 | +
|
| 77 | +**--all** |
| 78 | +> Run the command on every configured server in parallel. Same **proxmox** restriction as **--server**. |
| 79 | +
|
| 80 | +# COMMANDS |
| 81 | + |
| 82 | +**init** |
| 83 | +> Interactive wizard that creates or updates the configuration file. |
| 84 | +
|
| 85 | +**status** |
| 86 | +> CPU, memory, disk, and uptime. |
| 87 | +
|
| 88 | +**report** |
| 89 | +> Snapshot current system, container, and port state, compare it to the previous snapshot, and print a butler-style summary. Snapshots live under **~/.homebutler/reports/snapshots/** and are pruned to **--keep** entries (default **30**). **--no-save** prints without writing a snapshot. Change kinds in the report (and in **--json**) are **gone**, **new**, **replaced**, **image**, **state**, **port**, **disk**, and **skipped**. |
| 90 | +
|
| 91 | +**doctor** |
| 92 | +> Read-only preflight: resource pressure, stopped containers, public bind ports, backup hygiene, notification and watch readiness, report baseline, config-file permissions, Docker-socket mounts, Proxmox TLS/reachability, and incident-history limits. **--strict** exits non-zero on warnings or failures. **--backup-max-age** (default **168h**) warns when the latest backup is older than that. Unlike other commands, **doctor** will still run against a config that **Load** refuses for world-readable secrets, so it can name that failure. |
| 93 | +
|
| 94 | +**config validate** |
| 95 | +> Check the config without starting anything. Reports which file was used, which resolution rule picked it, what each section became, and keys that do not match the schema (otherwise dropped silently). **--strict** also fails on warnings. No remote routing. |
| 96 | +
|
| 97 | +**inventory scan** / **inventory show** |
| 98 | +> Tree of system health, Docker containers, and ports. **--filter exposed** keeps only ports listening on all interfaces (`0.0.0.0`, `::`, `*`). **--filter** cannot be combined with **--json**. |
| 99 | +
|
| 100 | +**inventory export** |
| 101 | +> Export the inventory. **--format mermaid** (default). |
| 102 | +
|
| 103 | +**docker** **list** | **restart** _c_ | **stop** _c_ | **logs** _c_ [_lines_] | **stats** | **top** _c_ | **inspect** _c_ |
| 104 | +> Container operations. **list** aliases **ls**. **logs** defaults to **50** lines. **inspect** never prints environment-variable values. **top** is read-only (`docker top`). |
| 105 | +
|
| 106 | +**install** _app_ |
| 107 | +> Generate Compose and deploy a catalogued self-hosted app (Uptime Kuma, Jellyfin, Pi-hole, Gitea, Portainer, and others). **--port**, **--media** (Jellyfin/Plex), **--dry-run**. Subcommands: **list**, **status** _app_, **uninstall** _app_ (keep data), **purge** _app_ (delete data). |
| 108 | +
|
| 109 | +**backup** |
| 110 | +> Tar Docker service volumes. **--service** _name_, **--to** _dir_. **backup list** lists archives. **backup drill** _service_ boots the latest (or **--archive**) backup in isolation; **--all** drills every supported app in the archive. |
| 111 | +
|
| 112 | +**restore** _archive_ |
| 113 | +> Restore volumes from an archive. **--service** _name_. Bind-mount host paths in the archive are refused unless named with repeatable **--allow-bind** _path_. |
| 114 | +
|
| 115 | +**watch** |
| 116 | +> Restart tracker. Records under **~/.homebutler/watch/**. Subcommands: **tui**, **add** [_name_] [**--kind** docker|systemd|pm2], **list**, **remove** _name_, **check**, **start** [**--interval**] (minimum **5s**), **history** (alias **incidents**), **show**, **install** / **uninstall** (supervisor service), **status**. Docker targets use `docker events`; systemd and PM2 poll. Watch notifications are **off** until **watch.notify.enabled** is set. |
| 117 | +
|
| 118 | +**serve** |
| 119 | +> Embedded web dashboard. **--host** (default **127.0.0.1**), **--port** (default **8080**), **--token** (bearer auth), **--demo** (no real system calls). Release binaries and **install.sh** embed the frontend; `go install` / a plain `go build` do not. |
| 120 | +
|
| 121 | +**mcp** |
| 122 | +> Model Context Protocol server on **stdio**. **--demo** uses fake data. |
| 123 | +
|
| 124 | +**ports** / **processes** / **network** |
| 125 | +> Open ports with owning processes (try **sudo** if names are missing), process list, and network scan. |
| 126 | +
|
| 127 | +**wake** _mac-or-name_ [_broadcast_] |
| 128 | +> Wake-on-LAN magic packet. Names resolve from the **wake** config section. Default broadcast **255.255.255.255**. |
| 129 | +
|
| 130 | +**notify test** |
| 131 | +> Send a test message through configured Telegram, Slack, Discord, or webhook providers. Legacy alias: **alerts test-notify**. |
| 132 | +
|
| 133 | +**alerts** |
| 134 | +> One-shot CPU/memory/disk threshold check. **--watch** loops (prefer **watch start** unless you want thresholds only). **--interval** (default **30s**). **alerts init**, **alerts history**. |
| 135 | +
|
| 136 | +**proxmox** |
| 137 | +> Inspect configured Proxmox VE endpoints (**status**, **guests**, **node**, **tasks**, **task**, **guest** start/shutdown/reboot, **script**). Use **--endpoint**, not **--server**. Guest power actions need a separate action token and **--confirm** with explicit **--node**, **--type**, and **--vmid**. |
| 138 | +
|
| 139 | +**deploy** / **upgrade** / **trust** / **version** |
| 140 | +> Copy the binary to remotes, upgrade it, manage host-key trust, and print the version. |
| 141 | +
|
| 142 | +# DESCRIPTION |
| 143 | + |
| 144 | +**homebutler** is a single Go binary for homelab operations. It remembers what a server looked like last time and reports **what changed**, rather than another graph of the current moment. There is no required daemon, database, or always-on web service: the same binary is the CLI, an optional dashboard, and an MCP server. |
| 145 | + |
| 146 | +The design is CLI-first and JSON-friendly so the same commands work from a terminal, cron, SSH, CI, or an agent that should not be given a full shell. **report** compares full container, process, and port lists (not just counts), so a container recreated under the same name shows as **replaced** rather than "no change". When a comparison cannot be made (for example Docker was down), the kind is **skipped** instead of an all-clear. |
| 147 | + |
| 148 | +Remote servers in the config are reached over SSH. Prefer key auth. Colour in terminal reports is dropped when stdout is not a TTY. |
| 149 | + |
| 150 | +# CONFIGURATION |
| 151 | + |
| 152 | +**~/.config/homebutler/config.yaml** |
| 153 | +> Preferred config (XDG). Created by **init**. Example sections: **servers**, **proxmox**, **wake**, **alerts**, **notify**, **watch**, **backup_dir**. Output format is a flag (**--json**), not a config key. |
| 154 | +
|
| 155 | +**$HOMEBUTLER_CONFIG** |
| 156 | +> Overrides the default path when **--config** is omitted. |
| 157 | +
|
| 158 | +**./homebutler.yaml** |
| 159 | +> Last-resort path in the current directory. |
| 160 | +
|
| 161 | +**~/.homebutler/reports/snapshots/** |
| 162 | +> **report** baselines. Pruned with **--keep**. |
| 163 | +
|
| 164 | +**~/.homebutler/watch/** |
| 165 | +> Watch list, incident history, and watch-local config. A running **watch** service reads the list at startup; **watch add** / **remove** print the restart command if a supervisor is installed. |
| 166 | +
|
| 167 | +**~/.homebutler/alerts.yaml** |
| 168 | +> Deprecated fallback for notify/rules. Move them into **config.yaml**. |
| 169 | +
|
| 170 | +A config that holds plaintext secrets must not be group/world-readable; **Load** refuses it (mode check on **perm & 0o077**). Unrecognised keys are ignored unless you run **config validate**. Check a file with **homebutler config validate**. |
| 171 | + |
| 172 | +# CAVEATS |
| 173 | + |
| 174 | +`go install github.com/Higangssh/homebutler@latest` builds without the embedded dashboard: **serve** then cannot serve `/`. Use a release binary, **install.sh**, or **make build-all** from a checkout. |
| 175 | + |
| 176 | +**watch.notify.enabled** defaults to false, and **notify_on: flapping** means a single restart is not sent. Configuring Telegram and passing **notify test** is not enough for incidents to be delivered. |
| 177 | + |
| 178 | +**doctor --strict** makes outbound calls to each configured Proxmox endpoint. An unreachable or rebooting host fails the run. |
| 179 | + |
| 180 | +Proxmox guest start/reboot/shutdown do not fall back to the read token. They fail until **action_token_id** and **action_token** (or **action_token_file**) are set, and they require **--confirm**. |
| 181 | + |
| 182 | +**install portainer** mounts the Docker socket (host root from inside the container). **doctor** reports that class of mount. |
| 183 | + |
| 184 | +# HISTORY |
| 185 | + |
| 186 | +**HomeButler** is an MIT-licensed Go project by **Higangssh**. The public repository was created on **23 February 2026** with **v0.1.0** the same day (core commands, network scan, alerts, Wake-on-LAN). **v0.2.0** added SSH multi-server support, XDG config discovery, and **deploy**. Later 2026 releases added the MCP server, **report** identity-aware diffs (**replaced** vs count comparison, **0.26.0**), Proxmox, backup drills, and the embedded dashboard. Current mainline at documentation time is **0.29.0**. |
| 187 | + |
| 188 | +# SEE ALSO |
| 189 | + |
| 190 | +[docker](/man/docker)(1), [docker-compose](/man/docker-compose)(1), [lazydocker](/man/lazydocker)(1), [ctop](/man/ctop)(1), [glances](/man/glances)(1), [wakeonlan](/man/wakeonlan)(1) |
| 191 | + |
| 192 | +# RESOURCES |
| 193 | + |
| 194 | +```[Source code](https://github.com/Higangssh/homebutler)``` |
| 195 | + |
| 196 | +```[Homepage](https://homebutler.dev)``` |
| 197 | + |
| 198 | +```[Documentation](https://github.com/Higangssh/homebutler#readme)``` |
| 199 | + |
| 200 | +<!-- verified: 2026-09-08 --> |
0 commit comments