Komodo Deployment Guide¶
This repository uses Komodo for declarative container orchestration via Git-ops.
Architecture Overview¶
graph TD
CORE["Komodo Core (Web UI)
https://komo.do / Self-hosted"]
CORE -->|WebSocket :9120, REST API| PER["Periphery Agent
(one per server, via systemd)"]
PER --> UNR["unraid (stacks)"]
PER --> DEV["development (dev only)"]
PER --> DB["database (disabled)"]
Directory Structure¶
.
├── komodo/
│ └── stacks/
│ ├── root_syncs/ # Core infrastructure
│ │ ├── resource_sync.toml # One sync per category
│ │ ├── server.toml # Server definitions
│ │ ├── alerter.toml # Ntfy alerting
│ │ └── procedure.toml # Scheduled tasks
│ │
│ ├── applications/ # App stacks (auto-generated)
│ ├── database/
│ ├── development/
│ ├── llm/
│ ├── monitoring/
│ ├── networking/
│ ├── orchestration/
│ └── storage/
│
├── stacks/ # Docker Compose source of truth
│ └── {category}/
│ └── {service}/
│ └── compose.yaml
│
└── dockerfiles/ # Custom build Dockerfiles
Resource Sync Flow¶
graph LR
A[Git Repo] -->|push| B[Webhook]
B -->|trigger| C[Komodo Core]
C -->|sync| D[resource_sync.toml]
D -->|diff| E[Pending Changes]
E -->|manual confirm| F[Deploy Stack]
Each category has its own resource_sync pointing to its TOML folder:
| Sync Name | Path | Purpose |
|---|---|---|
root-syncs |
./komodo/stacks/root_syncs |
Infrastructure (servers, alerter, procedures) |
applications |
./komodo/stacks/applications |
User-facing applications |
database |
./komodo/stacks/database |
Database services |
development |
./komodo/stacks/development |
Dev environments (Coder, Gitea, etc.) |
llm |
./komodo/stacks/llm |
LLM services (Ollama, LiteLLM, etc.) |
monitoring |
./komodo/stacks/monitoring |
Observability stack |
networking |
./komodo/stacks/networking |
Network services (Pi-hole, AdGuard, etc.) |
orchestration |
./komodo/stacks/orchestration |
Workflow engines (Airflow, n8n, Dagster) |
storage |
./komodo/stacks/storage |
Storage services (MinIO, SFTPGo, etc.) |
Adding a New Stack¶
1. Create Docker Compose¶
2. Create Komodo TOML¶
Create komodo/stacks/{category}/{service}.toml manually. Copy from an existing stack as template:
[[stack]]
name = "service-name"
deploy = true
tags = ["server:unraid", "category:{category}"]
[stack.config]
server = "unraid"
linked_repo = "docker-compose"
run_directory = "/etc/komodo/repos/docker-compose/stacks/{category}/{service}/"
file_paths = ["compose.yaml"]
See deployment-guide.md for the full template with all options.
3. Customize Tags¶
Edit the TOML and add meaningful tags:
[[stack]]
name = "my-service"
deploy = true
tags = [
"server:unraid",
"category:applications",
"saas:tailscale", # Expose via Tailscale
"selfhost:photo", # Self-hosted category
]
4. Common Tag Conventions¶
| Tag Pattern | Example | Meaning |
|---|---|---|
server:{name} |
server:unraid |
Target server |
category:{name} |
category:applications |
Stack category |
saas:{name} |
saas:tailscale |
Expose via SaaS (Tailscale) |
selfhost:{name} |
selfhost:photo |
Self-hosted replacement for SaaS |
app:{name} |
app:immich |
Application identifier |
Stack TOML Reference¶
[[stack]]
name = "service-name"
deploy = true
tags = ["server:unraid", "category:applications"]
[stack.config]
server = "unraid"
# Update behavior
poll_for_updates = false # Check but don't auto-deploy
auto_update = true # Auto-deploy on new image digest
destroy_before_deploy = true # Remove container before redeploy
auto_update_all_services = false # Only update changed services
# Source
linked_repo = "docker-compose" # Komodo repo name
run_directory = "./stacks/applications/my-service/"
file_paths = ["compose.yaml"]
additional_env_files = ["compose.env"] # Optional env file
# Pre-deploy script (optional)
pre_deploy.command = """
#!/usr/bin/env bash
set -eu pipefail
echo "Running before deploy..."
"""
# Environment - use [[VAR]] for secrets from Komodo
environment = """
TZ=Asia/Ho_Chi_Minh
DB_PASSWORD=[[DB_PASSWORD]]
"""
Environment Variables¶
Secret Interpolation¶
# In TOML, secrets use [[VAR]] syntax:
environment = """
DB_PASSWORD=[[DB_PASSWORD]]
API_KEY=[[API_KEY]]
"""
# Komodo resolves these from:
# 1. Komodo Variables UI (for non-secret values)
# 2. Core config file secrets block
# 3. Periphery config file secrets block
compose.env Usage¶
For local overrides, create compose.env in the stack directory:
Server Configuration¶
Servers are defined in komodo/stacks/root_syncs/server.toml:
[[server]]
name = "unraid"
description = "Main NAS server"
tags = ["server:unraid", "category:root_syncs"]
[server.config]
address = "http://100.68.251.84:8120"
enabled = true
[[server]]
name = "development"
description = "Dev VM"
tags = ["server:development", "category:root_syncs"]
[server.config]
address = "http://100.126.172.96:8120"
enabled = true
Alerting¶
Alerter is configured in komodo/stacks/root_syncs/alerter.toml:
[[alerter]]
name = "ntfy"
[alerter.config]
enabled = true
endpoint.type = "Ntfy"
endpoint.params.url = "http://development:9282/komodo"
Attaching Alerter to Stack¶
Add alerter tag to route alerts:
Deployment Dependencies¶
Use after to order deployments across services:
[[stack]]
name = "postgres"
deploy = true
[stack.config]
# ...
[[stack]]
name = "myapp"
deploy = true
after = ["postgres"] # Deploy after postgres
[stack.config]
# ...
Development Workflow¶
Local Development Loop¶
# 1. Edit compose.yaml locally
vim stacks/applications/myapp/compose.yaml
# 2. Test locally
cd stacks/applications/myapp
docker compose up -d
# 3. Commit and push
git add stacks/applications/myapp/
git commit -m "feat: update myapp"
git push
# 4. Komodo syncs via webhook and shows pending changes
# 5. Review in Komodo UI and confirm deploy
Forcing a Redeploy¶
# Via Komodo CLI
km deploy stack myapp
# Or via Procedure (run immediately)
km run action trigger-redeploy -y
Maintenance¶
Backup¶
Backups run daily via Backup Core Database procedure:
Rotate Periphery Keys¶
Update All Stacks¶
Network Architecture¶
graph TD
EXT["External (Internet)"] -->|HTTPS| TS["Tailscale VPN Network"]
TS --> NOTE["Domain pattern: {service}.{MACHINE_NAME}.tailXXXX.ts.net
Internal: {service}.home → Caddy (192.168.100.10)"]
CI/CD Integration¶
Custom Docker images are built via Komodo Build resources and pushed to the self-hosted Gitea registry:
graph LR
DF["stacks/{category}/{service}/Dockerfile"] --> KB[Komodo Build]
KB --> REG["git.minhluc.info/lucndm/{service}:latest"]
REG --> DEPLOY[Stack deploy]
The build pipeline is configured in Komodo UI (Build resources), not in this repository. Builds are triggered automatically when the Dockerfile changes in the linked repo.
See Custom Image Build Workflow for the full pattern.
Troubleshooting¶
Periphery Not Connecting¶
# Check periphery status
sudo systemctl status periphery
# View logs
sudo journalctl -u periphery -f
# Restart
sudo systemctl restart periphery
Stack Won't Deploy¶
# Check sync status
km x commit root-syncs # Force sync
# View pending changes
# In Komodo UI > Resource Sync > {sync-name}
View Container Logs¶
Quick Reference¶
| Task | Command |
|---|---|
| Deploy a stack | km deploy stack <name> |
| View stack logs | km-logs -S <name> |
| Sync resources | km x commit <sync-name> |
| Run procedure | km run procedure <name> |
| Set variable | km set var <KEY> <value> |
| SSH to server | km ssh <server> |
| Exec into container | km exec <container> bash |