A hard fork of Komodo v2.3.3 for podman: stacks are directories of quadlet files, agents only dial out, and stack secrets come from a vault at deploy time instead of the database. GPL-3.0-or-later, same as upstream. Quomodo is an independent project, not affiliated with or endorsed by Komodo or moghtech.
One line of gitea.container changes and gets pushed. The webhook deploys and only gitea.service restarts. Click for the video (0:22).
It runs one home lab today. It is young (0.x) and the config format may still change between releases.
| Servers | A Stack | A deploy |
|---|---|---|
![]() |
![]() |
![]() |
The pictures come from scripts/demo.sh, a throwaway lab of four podman
hosts in docker.
- Core (
quomodo): the API and UI. Runs as a root quadlet pod with mongo. - Agent (
quomodo-agent): runs on every host and dials out to Core overwss://only. No inbound listener, no passkeys, no terminals. Run as root it deploys rootful quadlets; run as a user it deploys that user's rootless ones withsystemctl --user. - Stacks: a directory in git holding raw
.pod,.container,.network,.volume,.build,.imageor.kubefiles plus config files. Deploying copies the directory to/etc/containers/systemd/<stack>/, reloads systemd and restarts the units whose files changed. A changed file restarts the units that bind-mount it, and a change to a file a.buildcontext sends (minus its.containerignore/.dockerignore) rebuilds the image and restarts its containers. Binary files and files over 1 MiB are copied as is, never interpolated. - User namespaces: every
.pod, and every.containeroutside a pod, runs in its own user namespace on a fixed 65536-id slice of the host'scontainerssubordinate range. The agent picks the slice the first time it sees the unit, records it in/var/lib/quomodo/idmap.json, and writes it asUIDMap=/GIDMap=, so a redeploy or a reboot never moves a container's root to another host uid (podman'sUserNS=autodoes, and files in volumes turn intonobody).UserNS=auto:uidmapping=...keeps its carve-outs;UserNS=hostand friends are left alone; a pod member shares the pod's namespace. Podman's:idmapvolume option solves the same problem per mount, and the agent passes it through as written. - Secrets:
[[NAME]]in a stack file is always a secret, read from the secret store named by the Stack'ssecrets_collection. Core fetches the values at deploy time, sends them to the agent over the key-authenticated websocket, and the agent creates podman secrets. A secret may only be referenced on aSecret=line; a config file holding a secret reference is mounted withSecret=file:<path>and never written under/etc. Nothing with a resolved secret touches mongo. First provider: Vaultwarden or Bitwarden, over the Bitwarden vault protocol.
Resources: Server, Stack, Repo, ResourceSync, Procedure, Action, Alerter. Auth: local, OIDC, API keys. Webhooks: GitHub (Forgejo and Gitea send the same format) and GitLab. Alerters send alerts to a Custom webhook, Slack, Discord, ntfy or Pushover, with their tokens kept in the secret store.
Gone from upstream: Deployment, Build, Builder, Swarm, Variables,
Google and GitHub OAuth, terminals and container exec, docker and compose,
image update polling. The Rust module names inside still say komodo and periphery,
and config environment variables still start with KOMODO_ / PERIPHERY_.
There is no migration from Komodo: stacks have to be rewritten as quadlets.
- Install: Core, agents, the first stack, webhooks
- Stacks: files, secrets, what a deploy restarts, user namespaces
- Secrets: providers and the vault service account
- Alerters: where alerts go, and keeping webhook tokens out of the database
- Releasing: tags, images, binaries
- Security: trust model and reporting
- Contributing
- Code of Conduct
bin/core,bin/periphery(the agent),bin/cli(km)client/core/rstypes,client/core/tsthe TypeScript client the UI useslib/quadletunit parsing and the interpolation rules shared by core and agentconfig/core.config.toml,config/agent.config.tomldocumented defaultsexample/quomodo-corethe core pod as quadlet filesexample/stacka sample stack directoryscripts/e2e.shthe end-to-end test
cargo build --release -p komodo_core -p komodo_periphery -p komodo_cli
cd client/core/ts && yarn && yarn build && yarn link
cd ui && yarn link komodo_client && yarn && yarn build
Tagged releases publish the agent binary for x86_64 and aarch64 as release
assets and the Core image for amd64 and arm64 to ghcr.io/loganrupe/quomodo
(releasing).
Pinned at v2.3.3. Security fixes are cherry-picked by hand; there are no tracking merges.


