Skip to content

Home Lab

Self-hosted infrastructure, containers, CI/CD, and operations.

Runs on a Synology DS923+ NAS. Stacks, builds, procedures and alerters are declared in Komodo TOML and synced from a git repo; secrets stay in env files kept out of it.

4
Stacks
14
Containers
Glossary
KomodoDocker management platform. Stacks, builds, procedures and alerters are defined in TOML and read from a git repo.
PeripheryKomodo agent on the Docker host. Runs builds, deploys stacks, executes shell actions. Authenticates by passkey.
ResourceSyncKomodo's diff-and-apply loop. Watches the komodo-configs repo and reconciles live resources against the TOML.
ProcedureMulti-stage automation in Komodo. Executions inside a stage run in parallel; stages run in order.
WatchtowerImage updater. Pulls newer tags and restarts the container. Opt-in per container by label.

Architecture

Komodo splits into a core that holds state and serves the API and a periphery agent that does the work on the Docker host. Synology's reverse proxy terminates TLS and routes by virtual host, so no container publishes a port to the network.

Users
Browser, any device on the local network
HTTPS
Synology Reverse Proxy
DSM Application Portal: TLS termination and virtual-host routing. No container publishes a port of its own.
Synology DS923+ NAS
Komodo
komodo-coreUI + API
komodo-peripheryAgent
mongo:7Resource state
Gitea
giteaGit
act_runnerCI
postgres:14Metadata
Immich
immich-serverAPI + Web
immich-microservicesWorkers
immich-mlInference
redisJob queue
pgvecto-rsVector store
immichframeDisplay
Finance
actual-serverLedger
stashNext.js
Cross-cutting
DSM SSO
OIDC
One identity for Komodo and the Finance apps
Watchtower
Docker API
Tag updates, databases excluded by label
Periphery
Docker API
Builds images and deploys stacks in place
One node, so a stack update is downtime and the host is a single point of failure. The trade taken was a rebuild path rather than a second machine.

Stacks

Each stack below is one block in resources.toml. Select a stack to see what it runs and how it is checked.

Komodo Infrastructure

The management plane. Core serves the UI and API behind OIDC; periphery does the work on the Docker host; mongo holds resource state that git is the source of truth for.

komodo-core
ghcr.io/moghtech/komodo-core
UI and API server, OIDC authentication
healthcheck: HTTP
komodo-periphery
ghcr.io/moghtech/komodo-periphery
Agent: builds images, deploys stacks, runs shell actions
komodo-mongo
mongo:7
Resource state, reconciled from the config repo

CI/CD and Infrastructure as Code

Two webhook-driven pipelines. A code push builds images in parallel and redeploys the stack; a config push runs ResourceSync against resources.toml.

Application pipeline

Code push to image on the same host it will run on.

Push to main
Gitea, application repo
Webhook
Komodo API
Runs the procedure named in the hook
Komodo Procedure
build-and-deploy-finance
Stage one builds, stage two deploys
Build image
stash:latest
Build complete
Deploy Finance stack
docker compose up against the declared config
Redeploy
Compose only recreates containers whose image changed, so the databases stay up through a deploy. That is also why they are excluded from Watchtower: an unattended image bump on Postgres is an unattended migration.

Infrastructure loop

Config push to reconciled infrastructure, no imperative step.

Push to komodo-configs
resources.toml
Webhook
PullRepo
Fetches the commit onto the host
ResourceSync
Diff and apply
Live resources reconciled against the file
Fan-out
Stacks
Compose files
Builds
Image configs
Procedures
Automation
Alerters
Notifications
The file is the intent and the running containers are the state, so a drifted resource is reconciled rather than reported. The cost is that a bad commit applies as readily as a good one.

Automation and Recovery

Database Backups
Weekly
pg_dump of every PostgreSQL database
A Komodo procedure runs pg_dump inside each database container, gzips the output, and writes a timestamped file to a NAS volume.
Docker Cleanup
Weekly
Prune orphaned images, containers and volumes
Runs after the backup stage so a prune can never race a dump. Local builds accumulate layers faster than anything else on the box.
Watchtower Updates
Continuous
Pull and restart on a new image tag
Opt-in by label. Database containers are excluded: image tags on Postgres are pinned and upgraded by hand.
Alerter Relay
Real-time
Komodo alerts forwarded to chat
A small Flask service receives the Komodo webhook, attaches CPU and memory figures, and posts to a chat channel.
Disaster Recovery

Full rebuild from the komodo-configs repo, the env files and a backup restore. The last step verifies it: health endpoints answer, an SSO login completes, a webhook fires. One TOML file, and a recovery procedure I have run end to end.

1
Deploy Komodo
One docker compose up brings the management plane back
2
ResourceSync
Repo sync recreates stacks, builds, procedures and alerters from TOML
3
Inject secrets
Env files are restored per stack: they are kept out of the config repo
4
Deploy stacks
Komodo brings each stack up against the declared config
5
Restore data
Database dumps and NAS volume snapshots for stateful services
6
Verify
Health endpoints answer, an SSO login completes, and a webhook fires, not finished until all three pass

Decisions

Platform

Komodo over Portainer

Moved off Portainer so that stacks, builds, procedures and alerters live in a git-tracked TOML file.

Why

Portainer kept its state in its own database, so every change was a click nobody could review or replay. Komodo reads resource definitions from a repo and reconciles on push, which makes the environment reproducible from source. The cost is another thing to operate: Komodo is itself three containers, and while it is down the fallback is docker compose over SSH.

One-way door. Every stack, build, procedure and alerter is written in Komodo's TOML schema, so leaving means rewriting the definitions rather than porting them.
1 / 5