ConfigHub Documentation: For detailed ConfigHub concepts (spaces, units, workers, functions, changesets, helm, packages), see docs.confighub.com. This document focuses on DevOps app architecture principles.
These are the fundamental principles that every DevOps app in our architecture must follow. They're derived from real implementation experience and the global-app canonical patterns.
The Most Important Principle: Every Kubernetes component (deployment, service, configmap, secret, etc.) is a separate ConfigHub Unit. The app itself is the collection of these units, grouped via Labels and Filters.
App = Collection of Units with same labels
├── Unit: namespace.yaml (label: app=myapp)
├── Unit: rbac.yaml (label: app=myapp)
├── Unit: configmap.yaml (label: app=myapp)
├── Unit: service.yaml (label: app=myapp)
└── Unit: deployment.yaml (label: app=myapp)
- Granular Control: Update individual components without touching others
- Bulk Operations: Apply changes to all units in the Set at once
- Atomic Deployments: All units succeed or fail together
- Version Tracking: Each unit has its own revision history
- Flexible Composition: Mix and match units across environments
# In bin/install-base
PROJECT=$(cub space new-prefix)
# Each Kubernetes component becomes a separate Unit with labels
cub unit create namespace k8s/namespace.yaml \
--space $PROJECT-base \
--label app=myapp --label type=infra
cub unit create deployment k8s/deployment.yaml \
--space $PROJECT-base \
--label app=myapp --label type=app
cub unit create service k8s/service.yaml \
--space $PROJECT-base \
--label app=myapp --label type=app
# Create filter to target all app components
cub filter create myapp-all Unit \
--where "Labels.app = 'myapp'" \
--space $PROJECT
# Create filter for just workloads
cub filter create myapp-workloads Unit \
--where "Labels.app = 'myapp' AND Labels.type = 'app'" \
--space $PROJECT// Get all units for this app via filter
units, _ := app.Cub.ListUnits(sdk.ListUnitsParams{
SpaceID: spaceID,
Where: "Labels.app = 'myapp'",
})
// Update just the deployment unit
app.Cub.UpdateUnit(spaceID, deploymentUnitID, sdk.UpdateUnitRequest{
Data: newDeploymentYAML,
})
// Bulk update all app workloads
app.Cub.BulkPatchUnits(sdk.BulkPatchParams{
SpaceID: spaceID,
Where: "Labels.app = 'myapp' AND Labels.type = 'app'",
Patch: imageUpdatePatch,
})
// Apply all app units
app.Cub.BulkApplyUnits(sdk.BulkApplyParams{
SpaceID: spaceID,
Where: "Labels.app = 'myapp'",
})drift-detector = Units with label 'app=drift-detector'
├── namespace.yaml (labels: app=drift-detector, type=infra)
├── rbac.yaml (labels: app=drift-detector, type=infra)
├── configmap.yaml (labels: app=drift-detector, type=config)
├── service.yaml (labels: app=drift-detector, type=app)
└── deployment.yaml (labels: app=drift-detector, type=app)
Each component can be:
- Updated independently (patch just the deployment)
- Versioned separately (configmap v1, deployment v2)
- Applied in order (namespace first, deployment last)
- Filtered by labels (update all workloads)
- Promoted as a group (the entire Set)
Workers are the bridge between ConfigHub (control plane) and Kubernetes (data plane). Without a worker, units exist in ConfigHub but never deploy.
cub worker create devops-worker --space $PROJECT
cub worker run devops-worker # Keep running!Follow the canonical patterns from global-app:
- Unique prefix generation:
cub space new-prefix - Space hierarchy: base → dev → staging → prod
- Filters for targeting
- Upstream/downstream relationships
ConfigHub units must contain properly formatted Kubernetes manifests with all required fields.
Each unit needs a target to know where to deploy:
cub target create k8s-target \
'{"KubeContext":"kind-cluster","KubeNamespace":"default"}' \
devops-worker --space $PROJECTThree authentication layers:
- ConfigHub:
cub auth login - Claude AI:
CLAUDE_API_KEY - Kubernetes:
KUBECONFIG
Use Kubernetes informers for immediate reaction:
app.RunWithInformers(func() error {
// React to changes immediately
return processChanges()
})Never use polling loops with time.Sleep.
Every app must have a demo mode for testing without infrastructure:
if len(os.Args) > 1 && os.Args[1] == "demo" {
return RunDemo()
}Always clean up old resources before creating new ones:
#!/bin/bash
# CRITICAL: Clean up old resources first
if [ -e ".cub-project" ]; then
OLD_PROJECT=$(cat .cub-project)
cub space delete $OLD_PROJECT 2>/dev/null || true
fi
# Now create new resources...NEVER use kubectl for modifications. All changes go through ConfigHub:
- ✅
cub unit update backend --patch - ❌
kubectl scale deployment backend
Every DevOps app deploys itself through ConfigHub:
bin/install-base # Create ConfigHub structure
bin/install-envs # Set up environments
bin/apply-all dev # Deploy via ConfigHubUse the compliance checker to verify:
./test-app-compliance-quick.sh my-app/Key checks:
- App organized as Set (PRINCIPLE #0)
- No kubectl in code
- Uses bulk operations
- Has demo mode
- Cleanup-first in scripts
- Event-driven architecture
- Sets Enable Bulk Operations: Manage entire apps, not individual configs
- ConfigHub as Truth: Single source prevents drift
- Event-Driven Beats Polling: Instant reaction, lower resource usage
- Self-Deployment: Apps manage their own lifecycle
- Demo Mode: Test without infrastructure costs
The global-app example demonstrates all principles working together:
- App defined as a Set
- Filters for targeting subsets
- Bulk operations for efficiency
- Environment hierarchy with upstream
- Push-upgrade for propagation
Study /Users/alexisrichardson/github-repos/confighub-examples/global-app/ for the canonical implementation.