An open-source home server inspired by kubernetes. Self-hosting is kind of unreasonably hard. Not the installing part. The part after.
In bloud, you install an app and the reverse proxy, unified login, and all inter-app integrations happen for you.
Debian 13, x86_64.
The script is short, and you should read it before piping it to a shell:
install.sh. To do it by hand instead, grab the .deb from
the releases page and run
sudo apt install ./bloud_*.deb.
curl -fsSL https://raw.githubusercontent.com/d-buckner/bloud/main/install.sh | sudo shOpen the dashboard at http://localhost:8080. Set your host under Settings, then Hosts. Install Jellyfin.
Please don't expose bloud to the public internet yet. It's alpha, it serves plain HTTP, and there is no mechanism yet for getting security updates to apps or to Bloud itself. Keep it on your LAN for now.
Getting a container running isn't the hard part, podman run will do that. The hard part is
everything the container needs from the rest of the system: a route in Traefik, an OIDC client
or an LDAP binding in Authentik, a database with a password nobody has to copy by hand, and all
of it still correct a year later.
Bloud handles that with a reconciliation loop, the same shape as a Kubernetes controller. Each app declares what it provides and what it consumes, the engine resolves those declarations into a graph, works out the wiring, and then keeps checking its work.
Two rules hold that together:
- Single writer. Only the orchestrator writes lifecycle state or performs side effects. HTTP handlers submit intents and never mutate anything themselves.
- Idempotent configurators.
PreStartandPostStartrun on every cycle, not just on install.PreStartbrings the config on disk in line with what the app should have, and does nothing when it already matches.
A template can write one config file once. Nothing but a loop keeps every config file in someone's homelab correct through upgrades, crashes, and reboots.
Every app, the containers each one declares, and the edges that connect them.
CI draws this rather than anyone updating it by hand. A headless browser renders it with the
same components that draw the developer graph inside a running Bloud, fed the catalog snapshot
that ./bloud depgraph --json builds from every app's metadata.yaml. The catalog is the only
input, so the picture can be drawn on a machine with nothing installed.
The text form of the same graph is in
docs/architecture/dependency-graph.md, and that is
what --write refreshes and --check gates. Add an app and both of them regenerate; a merge
that touches neither the catalog nor the renderer leaves both alone.
npm run graph:image # rebuild the picture locallyThe catalog is small on purpose. A half-supported app is worse than no app at all, because it looks like an answer right up until the first time you depend on it. Every entry here carries the same contract and we verify each one of them: install, shared login, persistence, reboot, removal.
./bloud catalogdoc --write generates the list below from every app's metadata.yaml, the
same file the graph is drawn from, so a new app shows up here whether or not anyone remembers
to mention it.
- AFFiNE: AI-native knowledge base that unifies docs, databases, and whiteboards
- Calino: Browser calendar for the CalDAV calendars Bloud already serves
- Hermes: Self-improving AI agent with persistent memory, scheduled automations, and a web dashboard
- Home Assistant: Open-source home automation platform
- Immich: Self-hosted photo and video management
- Jellyfin: Free software media system for streaming movies, TV, and music
- Navidrome: Modern music server and streamer compatible with Subsonic/Airsonic clients
- Paperless-ngx: Document management system that turns scans and PDFs into a searchable archive
- Prowlarr: Indexer manager that syncs indexers to Sonarr, Radarr, and other PVRs
- qBittorrent: BitTorrent client with a web interface
- Radarr: Movie collection manager for Usenet and BitTorrent users
- Radicale: CalDAV and CardDAV server for calendars, contacts, and to-do lists
- Seerr: Request and discovery manager for your media server
- Sonarr: PVR for TV series that monitors, grabs, and organises episodes
- Vaultwarden: Lightweight, Bitwarden-compatible password manager
Plus the system apps: Authentik (security) and Traefik (network).
The media stack is where this shows up most. Sonarr, Radarr, Prowlarr, qBittorrent, and Seerr are five separate projects that only become a pipeline once they're wired to each other, and that wiring is the tedious part by hand: add qBittorrent as a download client in each PVR with its own category and folder, get the indexers from Prowlarr into both, then connect Seerr to Jellyfin and to the PVRs so a request actually lands somewhere. Bloud does all of it from the declarations, so five apps is five clicks rather than an afternoon of copy-paste.
We ship no media, no indexers, and no trackers, and Bloud has no view on what you point it at. It's built for things you have the right to use.
| Strategy | Apps |
|---|---|
| LDAP | Jellyfin, Radicale, Seerr |
| Forward auth | Calino, Navidrome, Prowlarr, qBittorrent, Radarr, Sonarr |
| Native OIDC | AFFiNE, Hermes, Home Assistant, Immich, Paperless-ngx, Vaultwarden |
Clients that speak a native protocol, a Subsonic player or a TV app talking to Jellyfin, keep the login path their protocol defines. Bloud fronts the web UIs and stays out of the way of those.
Alpha in specific ways, so you know which gaps you're signing up for.
- No TLS. Plain HTTP only, and this is the biggest gap. Let's Encrypt on Traefik, or Tailscale Serve, is the planned follow-up. Fine on your LAN if you accept it; not acceptable off-LAN.
- Sharing in progress. Core sharing works. Tailnet outpost auth is still in development.
- No
bloud init. First-run host config happens in the dashboard. - Debian 13 only. A support contract has to be true somewhere before it spreads.
- The loop is not yet hardened against every failure mode. The auth bypass is remotely forgeable and is the current shipping blocker. Full ledger: docs/operations/tech-debt.md.
npm run setup # pick backend, check prereqs, build ./bloud
./bloud dev # build + deploy + run (Ctrl-C to stop)
./bloud install jellyfin # through the real API
./bloud validate --tier fastBackends: Lima on macOS (automatic), QEMU on Linux (default), native on Linux CI
(BLOUD_BACKEND=native). On the native backend ./bloud dev hot-reloads: save a
Go file and the host-agent rebuilds and restarts in about 3s with the app containers
left running, and the dashboard hot-reloads through vite. Use --no-watch for the
one-shot build-deploy-run. Add --reset to wipe the runtime first (the same wipe as
./bloud reset -y, no prompt) and come up from empty data. Apps land at
http://<app>.localhost:8080.
The integration tier runs the real graph path rather than a shortcut: host-agent deployed as a
systemd user service, Jellyfin installed through POST /api/apps/jellyfin/install, the
orchestrator converged, behavioral tests run inside the VM. Tests assert what the app's own API
reports, not what our config values happen to be.
./bloud validate --tier integration # real install/reconcile flow
./bloud e2e lifecycle # install -> restart -> uninstall -> cleanupWhile the high level technical design and architecture are done by me personally, much of the low level implementation is done by LLM. For me, this is done with local models hosted on my own hardware (qwen3.8-flash-next at the time of writing). If this does not align with the values you want your software to have, I understand and this project may not be for you.
- docs/README.md: index of all documentation
- docs/specs/spec.md: authoritative first-release plan
- docs/specs/reconciler-spec.md: reconciler subsystem design
- docs/architecture/overview.md: component overview
- docs/guides/contributing-apps.md: how to add an app
- docs/features/sharing.md: federated sharing design
AGPL v3. See LICENSE.
