Komodo
Webhook-driven Docker stack manager. Receives push webhooks from Forgejo, runs
git pull on the homelab-docker repo, then runs docker compose up -d for
each managed stack. Only containers whose config actually changed get restarted.
Where it runs
- Host: Nexus (
192.168.1.226) - UI: http://192.168.1.226:9120
- Compose file:
komodo/docker-compose.ymlin homelab-docker repo
Components
Three containers, all defined in komodo/docker-compose.yml:
| Container | Image | Role |
|---|---|---|
mongo |
mongo:8.3.2 |
Database — stores Komodo state (stacks, procedures, run history) |
core |
ghcr.io/moghtech/komodo-core:2.2.0 |
API server and orchestrator; exposes port 9120 |
periphery |
ghcr.io/moghtech/komodo-periphery:2.2.0 |
Executor that touches Docker and git on the host |
Periphery mounts the Docker socket and the homelab-docker repo directory so it
can run git pull and docker compose up -d on behalf of Core.
Deploy Procedure
Every push to main on Forgejo fires a webhook to Komodo, which runs a
two-stage procedure:
Stage 1 (sequential)
└── Pull Repo — git pull in /home/matt/repos/homelab-docker
Stage 2 (parallel)
├── Deploy Stack: dashy
├── Deploy Stack: bookstack
├── Deploy Stack: tandoor
├── Deploy Stack: homeassistant
└── Deploy Stack: pinchflat
Each Deploy Stack action calls docker compose up -d in the stack's service
directory. Docker only restarts containers whose parsed config changed.
Check run history: Komodo UI → Procedures → select the procedure → run history → expand stages.
What Komodo Does and Doesn't Manage
| Component | Managed? | Why |
|---|---|---|
| dashy, bookstack, tandoor, homeassistant, pinchflat | Yes | Deployed on every push to main |
| renovate | No | Cron-only; including it would trigger an unwanted scan on every push |
| komodo itself | Never | It would kill itself mid-deploy — update via SSH only (see below) |
| dispatcharr | Yes | Deployed via Periphery agent on LXC 113 (192.168.1.254) |
CRITICAL: Never Self-Deploy Komodo
Updating Komodo via its own deploy pipeline kills it mid-deploy. Update via SSH only:
ssh matt@192.168.1.226 'cd /home/matt/repos/homelab-docker/komodo && docker compose up -d'
The gitconfig Gotcha
Periphery runs as root inside the container, but the homelab-docker repo on the
host is owned by matt. Git's safe.directory check blocks this combination
and produces a misleading error (see Troubleshooting below).
The fix is komodo/.gitconfig, which is bind-mounted read-only as
/root/.gitconfig in the Periphery container:
[safe]
directory = /home/matt/repos/homelab-docker
directory = *
The directory = * wildcard is the safe option — Periphery runs as root and
owns the host system anyway, so this doesn't weaken security. After editing the
file on disk, the change is live immediately (no container restart needed).
Periphery on LXC 113 (Dispatcharr)
A second Periphery agent runs on PVE LXC 113 (192.168.1.254) to manage
Dispatcharr. It connects outbound to Komodo Core — no inbound port needs to be
open on the LXC.
- Compose file on LXC:
/opt/komodo/periphery.yml - Core endpoint:
ws://192.168.1.226:9120
Restart Periphery if it disconnects:
ssh root@192.168.1.227 "pct exec 113 -- docker compose -f /opt/komodo/periphery.yml restart"
Secrets
komodo/.env is gitignored and lives on Nexus only. It contains:
KOMODO_DATABASE_USERNAMEKOMODO_DATABASE_PASSWORD
No .env.enc encrypted backup exists yet for this file — this is a known gap.
If Nexus needs to be rebuilt, restore from PBS or recreate manually.
Forgejo Webhook
The webhook is configured in Forgejo under the homelab-docker repo → Settings → Webhooks. The payload URL format is:
http://192.168.1.226:9120/listener/github/procedure/<procedure-id>/main
The /main suffix is a branch filter — Komodo only executes when the push is
to main. The procedure ID is the MongoDB ObjectId visible in the Komodo UI
when viewing the procedure (Config tab → Webhook section shows the full URL).
Important: New procedures are created with webhook_enabled: false. After
creating a procedure, enable the "Webhook Enabled" toggle in the Config tab.
Without this, Komodo authenticates the webhook and silently does nothing.
API
The REST API is served at the root — no /api prefix.
Authentication requires both x-api-key and x-api-secret headers. Create
keys in Komodo UI → user profile → API Keys. Each key generates a key
(K_...) and a secret (S_...) — save the secret immediately, it is not shown
again.
# Full OpenAPI docs
curl http://192.168.1.226:9120/docs
# Trigger Pull Repo manually
curl -s -X POST "http://192.168.1.226:9120/execute/PullRepo" \
-H "x-api-key: <key>" \
-H "x-api-secret: <secret>" \
-H "Content-Type: application/json" \
-d '{"repo":"homelab-docker"}'
# Trigger the full deploy procedure
curl -s -X POST "http://192.168.1.226:9120/execute/RunProcedure" \
-H "x-api-key: <key>" \
-H "x-api-secret: <secret>" \
-H "Content-Type: application/json" \
-d '{"procedure":"<procedure-id>"}'
Backup
Komodo state lives in a named Docker volume (mongo-data). PBS backs up Nexus
automatically, which covers this volume.
Manual MongoDB dump (the backups dir is bind-mounted to
/mnt/server/containers/komodo/backups):
ssh matt@192.168.1.226 'docker exec komodo-mongo-1 mongodump --out /backups'
Troubleshooting
Procedure completes in ~1ms and deployed nothing
Stage 2 is misconfigured. It must have 5 explicit Deploy Stack actions. If it instead uses "Batch Deploy Stack If Changed," that action always skips because stacks have no linked repo hash to compare against.
Fix: Komodo UI → Procedures → edit Stage 2 → replace with 5 individual Deploy Stack actions (dashy, bookstack, tandoor, homeassistant, pinchflat).
Pull Repo fails — "fatal: not a git repository (or any parent up to mount point /home/matt/repos)"
The safe.directory check in git is blocking Periphery (runs as root) from
accessing a repo owned by matt. Check komodo/.gitconfig — it must contain:
[safe]
directory = /home/matt/repos/homelab-docker
directory = *
After editing, trigger a test pull to confirm: Komodo UI → Repos → homelab-docker → Pull. No container restart needed.
Webhook fires but stacks don't update
- Check Stage 1 run history — expand Pull Repo and confirm the "Latest Commit" hash matches HEAD on Forgejo. If not, the pull ran but didn't reach the remote.
- Verify Stack
run_directorysettings point to/home/matt/repos/homelab-docker/<service>.
Webhook fires but procedure never runs
The procedure has webhook_enabled: false (default for new procedures). Enable
"Webhook Enabled" in Komodo UI → Procedures → the procedure → Config tab.
Webhook didn't fire after a merge
- Check Forgejo delivery log: repo → Settings → Webhooks → recent deliveries
- Confirm Komodo is reachable:
curl http://192.168.1.226:9120 - Manual deploy:
ssh matt@192.168.1.226 'cd /home/matt/repos/homelab-docker/<service> && docker compose up -d'
Container logs
# Core
ssh matt@192.168.1.226 'docker logs komodo-core-1 --tail 50'
# Periphery
ssh matt@192.168.1.226 'docker logs komodo-periphery-1 --tail 50'
Automation
Infrastructure
Services
- Dashy
- Bookstack
- Tandoor
- Home Assistant
- Pinchflat
- FreshRSS
- Frigate
- Wyze-Bridge
- Mosquitto
- Dispatcharr
- Proxmox LXC Services
Operations
- How to Update Apps
- Backup & Restore Strategy
- Maintenance Schedule
- Useful Commands
- Restore Proxmox Containers
- NAS NFS Share for LXC
- SSH Public Key onto Nexus