Spec-driven infrastructure is the core principle behind kombify — you describe what you want, not how it should be implemented.
The Core Concept
What You Write
# This is ALL you write
version: "1.0"
stackkit: base-kit
meta:
name: my-homelab
domain: home.example.com
nodes:
- name: proxmox-01
type: hypervisor
connection:
host: 192.168.1.100
services:
- traefik # Reverse proxy
- authelia # Single sign-on
- homepage # Dashboard
- immich # Photo management
What kombify Generates
Docker Compose
Traefik Config
OpenTofu
version: "3.8"
services:
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik:/etc/traefik
- ./letsencrypt:/letsencrypt
networks:
- proxy
labels:
- "traefik.enable=true"
# ... 50+ more lines of configuration
authelia:
image: authelia/authelia:4.38
container_name: authelia
restart: unless-stopped
depends_on:
- traefik
volumes:
- ./authelia:/config
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.authelia.rule=Host(`auth.home.example.com`)"
# ... more configuration
# ... more services
networks:
proxy:
external: true
api:
dashboard: true
insecure: false
entryPoints:
http:
address: ":80"
http:
redirections:
entryPoint:
to: https
scheme: https
https:
address: ":443"
http:
tls:
certResolver: letsencrypt
certificatesResolvers:
letsencrypt:
acme:
email: admin@home.example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: http
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
network: proxy
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0"
}
}
}
resource "docker_network" "proxy" {
name = "proxy"
}
resource "docker_container" "traefik" {
name = "traefik"
image = "traefik:v3.0"
ports {
internal = 80
external = 80
}
ports {
internal = 443
external = 443
}
# ... extensive configuration
}
The Unifier Engine
The Unifier is the heart of kombify’s spec-driven approach. It transforms your intent into executable infrastructure.
Pipeline Stages
Parse
The YAML file is parsed into Go structs. Invalid YAML syntax is caught here.# Example error
Error: YAML parse error at line 15
Expected string value, got integer
Validate
Configuration is validated against CUE schemas from the selected StackKit.✓ Required fields present
✓ Service names valid
✓ Port ranges valid
✓ Dependencies resolvable
Resolve
Missing values are filled with StackKit defaults. Dependencies are resolved.# You wrote:
services:
- authelia
# Resolved to:
services:
authelia:
image: authelia/authelia:4.38
depends_on: [traefik, redis]
traefik: # Auto-added (dependency)
image: traefik:v3.0
redis: # Auto-added (dependency)
image: redis:7
Generate
Infrastructure as Code artifacts are generated:
- OpenTofu HCL files
- Docker Compose configurations
- Service-specific configs (Traefik rules, Authelia config, etc.)
Two-File System
kombify uses a clear separation between user intent and system output:
Never edit generated files directly. All changes should go through kombination.yaml. Generated files are overwritten on each deployment.
| File | Owner | Purpose |
|---|
kombination.yaml | You | Your infrastructure intent |
stack-spec.yaml | Unifier | Resolved, normalized configuration |
generated/* | Generator | OpenTofu, Compose, configs |
Validation in Action
Validation Output
$ kombify validate
Validating kombination.yaml...
✓ YAML syntax valid
✓ Version: 1.0 (supported)
✓ StackKit: base-kit (found)
✓ Required fields present
✓ Node 'proxmox-01': configuration valid
✓ Service dependencies:
├─ traefik: no dependencies
├─ authelia: depends on [traefik] ✓
├─ homepage: depends on [traefik] ✓
└─ immich: depends on [traefik, postgres] ✓
✓ Port conflicts: none detected
✓ Network configuration: valid
Validation successful! Ready to deploy.
Why Spec-Driven?
Reduced Complexity
Traditional: 500+ lines across multiple fileskombify: 20 lines in one fileYou describe intent, not implementation.
Consistency
Every change goes through the same pipeline:
- Validate
- Resolve
- Generate
- Apply
No manual drift. Reproducibility
Your kombination.yaml is the single source of truth:
- Version-controllable
- Shareable
- Disaster-recoverable
Example: Adding a Service
Without kombify
With kombify
- Research which image to use
- Find compatible configuration examples
- Create Docker Compose entry
- Configure Traefik labels for routing
- Set up SSL certificate
- Configure authentication with Authelia
- Add to Homepage dashboard
- Test everything works together
Time: 30-60 minutes# Add one line:
services:
- jellyfin
Time: 30 seconds
Next Steps
Explore StackKits
Learn about the pre-validated blueprints that power spec-driven infrastructure
Quick Start
Deploy your first stack in 5 minutes