An open-source home server inspired by kubernetes. Self-hosting is kind of unreasonably hard. Not the installing part, but everything 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 and that's it.
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.
flowchart TD
subgraph app_authentik["Authentik (system)"]
c_authentik_postgres["postgres"]
c_authentik_redis["redis"]
c_authentik_server["server"]
c_authentik_worker["worker"]
c_authentik_ldap["ldap"]
c_authentik_server --> c_authentik_postgres
c_authentik_server --> c_authentik_redis
c_authentik_worker --> c_authentik_postgres
c_authentik_worker --> c_authentik_redis
c_authentik_ldap --> c_authentik_server
end
subgraph app_traefik["Traefik (system)"]
c_traefik["traefik"]
end
subgraph app_affine["AFFiNE"]
c_affine_postgres["postgres"]
c_affine_redis["redis"]
c_affine["affine"]
c_affine --> c_affine_postgres
c_affine --> c_affine_redis
end
subgraph app_affine_mcp["AFFiNE MCP"]
c_affine_mcp["affine-mcp"]
end
subgraph app_arr_mcp["Arr MCP"]
c_arr_mcp["arr-mcp"]
end
subgraph app_calino["Calino"]
c_calino["calino"]
end
subgraph app_dav_mcp["DAV MCP"]
c_dav_mcp["dav-mcp"]
end
subgraph app_hermes["Hermes"]
c_hermes["hermes"]
end
subgraph app_hermes_webui["Hermes Web UI"]
c_hermes_webui["hermes-webui"]
end
subgraph app_homeassistant["Home Assistant"]
c_homeassistant["homeassistant"]
end
subgraph app_immich["Immich"]
c_immich_postgres["postgres"]
c_immich_redis["redis"]
c_immich_ml["ml"]
c_immich_server["server"]
c_immich_server --> c_immich_postgres
c_immich_server --> c_immich_redis
end
subgraph app_jellyfin["Jellyfin"]
c_jellyfin["jellyfin"]
end
subgraph app_navidrome["Navidrome"]
c_navidrome["navidrome"]
end
subgraph app_paperless_ngx["Paperless-ngx"]
c_paperless_ngx_postgres["postgres"]
c_paperless_ngx_redis["redis"]
c_paperless_ngx_gotenberg["gotenberg"]
c_paperless_ngx_tika["tika"]
c_paperless_ngx["paperless-ngx"]
c_paperless_ngx --> c_paperless_ngx_postgres
c_paperless_ngx --> c_paperless_ngx_redis
end
subgraph app_prowlarr["Prowlarr"]
c_prowlarr["prowlarr"]
end
subgraph app_qbittorrent["qBittorrent"]
c_qbittorrent["qbittorrent"]
end
subgraph app_radarr["Radarr"]
c_radarr["radarr"]
end
subgraph app_radicale["Radicale"]
c_radicale_pimsync["pimsync"]
c_radicale["radicale"]
c_radicale_pimsync --> c_radicale
end
subgraph app_seerr["Seerr"]
c_seerr["seerr"]
end
subgraph app_sonarr["Sonarr"]
c_sonarr["sonarr"]
end
subgraph app_vaultwarden["Vaultwarden"]
c_vaultwarden["vaultwarden"]
end
ai_model["AI Model"]
%% Cross-app integration edges
app_affine -->|native-oidc| app_authentik
app_affine -->|caldav| app_radicale
app_affine -->|inference| ai_model
app_affine_mcp -->|appApi| app_affine
app_arr_mcp -->|mediaServer| app_jellyfin
app_arr_mcp -->|pvr| app_radarr
app_arr_mcp -->|requestManager| app_seerr
app_arr_mcp -->|pvr| app_sonarr
app_calino -->|forward-auth| app_authentik
app_calino -->|caldav| app_radicale
app_dav_mcp -->|appApi| app_radicale
app_dav_mcp -->|caldav| app_radicale
app_hermes -->|mcp| app_affine_mcp
app_hermes -->|mcp| app_arr_mcp
app_hermes -->|native-oidc| app_authentik
app_hermes -->|mcp| app_dav_mcp
app_hermes -->|inference| ai_model
app_hermes_webui -->|agentApi| app_hermes
app_homeassistant -->|native-oidc| app_authentik
app_immich -->|native-oidc| app_authentik
app_jellyfin -->|ldap| app_authentik
app_navidrome -->|forward-auth| app_authentik
app_paperless_ngx -->|native-oidc| app_authentik
app_prowlarr -->|forward-auth| app_authentik
app_prowlarr -->|pvr| app_radarr
app_prowlarr -->|pvr| app_sonarr
app_qbittorrent -->|forward-auth| app_authentik
app_radarr -->|forward-auth| app_authentik
app_radarr -->|downloadClient| app_qbittorrent
app_radicale -->|ldap| app_authentik
app_radicale -->|icsFeed| app_radarr
app_radicale -->|icsFeed| app_sonarr
app_seerr -->|mediaServer| app_jellyfin
app_seerr -->|pvr| app_radarr
app_seerr -->|pvr| app_sonarr
app_sonarr -->|forward-auth| app_authentik
app_sonarr -->|downloadClient| app_qbittorrent
app_traefik -->|proxy| app_affine
app_traefik -->|proxy| app_affine_mcp
app_traefik -->|proxy| app_arr_mcp
app_traefik -->|proxy| app_authentik
app_traefik -->|proxy| app_calino
app_traefik -->|proxy| app_dav_mcp
app_traefik -->|proxy| app_hermes
app_traefik -->|proxy| app_hermes_webui
app_traefik -->|proxy| app_homeassistant
app_traefik -->|proxy| app_immich
app_traefik -->|proxy| app_jellyfin
app_traefik -->|proxy| app_navidrome
app_traefik -->|proxy| app_paperless_ngx
app_traefik -->|proxy| app_prowlarr
app_traefik -->|proxy| app_qbittorrent
app_traefik -->|proxy| app_radarr
app_traefik -->|proxy| app_radicale
app_traefik -->|proxy| app_seerr
app_traefik -->|proxy| app_sonarr
app_traefik -->|proxy| app_vaultwarden
app_vaultwarden -->|native-oidc| app_authentik
Each box is one app; the nodes inside it are that app's containers, with an arrow from a container to every container it depends on. Arrows between boxes are integrations: a proxy arrow is drawn from the proxy to the apps it routes, and an SSO arrow is labeled with the app's strategy (ldap, forward-auth, native-oidc). The AI Model node is outside every box because no app provides it: it is the instance's own Settings -> AI endpoint, and any app that declares the inference contract is wired to it.
The 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.
- AFFiNE: AI-native knowledge base that unifies docs, databases, and whiteboards
- AFFiNE MCP: Full read-write MCP tool server for AFFiNE, for agents that author documents, databases, and canvases
- Arr MCP: MCP tool server over the request and arr stack, so an agent can answer what was requested and what is stuck
- Calino: Browser calendar for the CalDAV calendars Bloud already serves
- DAV MCP: MCP tool server for the calendars and contacts Bloud already serves, so agents read and write events, to-dos, and address books
- Hermes: Self-improving AI agent with persistent memory, scheduled automations, and a web dashboard
- Hermes Web UI: Browser and mobile front end for the Hermes agent, with SSO sign-in and a dedicated client password
- 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, Hermes Web UI, Home Assistant, Immich, Paperless-ngx, Vaultwarden |
| App-local accounts | AFFiNE MCP, Arr MCP, DAV MCP |
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.
- 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
AGPL v3. See LICENSE.