Skip to content

Build1 publisher2 min readPublished

Cloud in a Bottle puts every self-hosted app in its own user namespace by default

Imbue's platform runs each of its 38 catalog apps as a rootless Podman container under a single router service on Ubuntu. The one published writeup that credits that design also says the recommended tunnel carries HTTP only.

The Engineer · Build desk

Illustration accompanying Cloud in a Bottle puts every self-hosted app in its own user namespace by default

What happened

  • Imbue's Cloud in a Bottle runs on one Ubuntu machine as a central router service, with a component called openhost.service managing the lifecycle of rootless Podman containers.
  • Requests are routed by inspecting domain headers and proxying to the matching container, and one login using persistent session cookies and scoped API tokens covers the whole suite.
  • The curated catalog holds 38 applications, among them Nextcloud, Jellyfin, Forgejo and Vaultwarden, with the selection heavily influenced by Imbue's own needs.
  • The recommended way to expose an instance is Cloudflare Tunnel, which carries HTTP only, so raw TCP services such as a Minecraft server cannot be published as documented.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint Anything that is not HTTP has to be published outside the platform to be reachable, so the per-app namespace protects services whose exposure path openhost.service is not supervising.
  • cost A resolver that must bind port 53 has to own it, so an operator either dedicates a machine to this platform or resolves the conflict before install.
  • decision Anyone weighing this against Cloudron, Umbrel or CapRover is trading years of refinement and a wider app list for namespaced apps out of the box.
  • exposure Because one login covers every app, a stolen session cookie reaches the whole suite, whatever namespace each container runs in.

Rootless Podman under a single supervising service is a sound way to build this. The isolation claim rests on one detail of the configuration. Per-app user namespaces only close the host hole if each app holds its own subordinate UID and GID range, so that a container root breaking out lands on an unprivileged host account that owns nothing else. If openhost.service runs every container under one shared service account instead, the blast radius is that account. That account holds the catalog. The post says openhost.service manages the lifecycle of rootless Podman containers and that each app runs within its own user namespace [3][4]; it does not publish the unit file or the subuid allocation.

The comparison to YunoHost is the dev.to writeup's own assessment. It calls the arrangement "a critical security win" and describes it as mitigating the "one broken app destroys the host" risk found in platforms like YunoHost [4]. Anyone who wants that verified has to install both and read their defaults.

The HTTP-only limit on Cloudflare Tunnel is why the post's own remedy comes from a vendor [9]. Its two example commands are `ssh -p 443 -R0:localhost:25565 [email protected]` for a raw TCP service and `ssh -p 443 -R0:localhost:8080 free.pinggy.io` for the dashboard on port 8080 [10][11]. They differ by the `tcp@` user and the forwarded port, so raw TCP is a separate endpoint mode from the web one [17]. The post calls the project documentation's discussion of networking its most honest part, and then names a tunnel provider as the fix [16][10].

The `bottle` CLI is aimed at coding agents, letting automation scripts deploy and manage containers without manual credential handling, and the documentation is served in machine-readable form at `/docs/all.md` [12][13]. An agent that can call `bottle` can create containers.

Feedback on the project also raised persistent storage as a reason for hesitation [15]. The catalog includes Vaultwarden [7], so where volumes live, and what happens to them when a container is replaced, is the first thing I would test on a box I intended to keep.

What to watch

  • Whether Imbue publishes the openhost.service unit file and the per-app subuid allocation, which is what makes the isolation claim checkable.
  • Whether the platform gains native raw TCP exposure instead of pointing users at a third-party tunnel.
  • Whether the CoreDNS port 53 requirement becomes optional, which decides if the host can be shared.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories