1 Komodo
Matt Cupp edited this page 2026-05-29 17:35:45 -04:00

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

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_USERNAME
  • KOMODO_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

  1. 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.
  2. Verify Stack run_directory settings 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

  1. Check Forgejo delivery log: repo → Settings → Webhooks → recent deliveries
  2. Confirm Komodo is reachable: curl http://192.168.1.226:9120
  3. 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'