Configuration
One file. Your entire environment.
LoKO uses a single loko.yaml file to define your complete local Kubernetes setup. Change the file, run loko config sync, and your environment updates. Infrastructure as code that actually works.
Why Declarative Configuration?
Section titled “Why Declarative Configuration?”The Old Way (Imperative Hell)
Section titled “The Old Way (Imperative Hell)”# Manually create clusterkind create cluster --config cluster.yaml
# Install ingress controllerkubectl apply -f https://raw.githubusercontent.com/...ingress-nginx.yaml# Wait for it...kubectl wait --for=condition=ready pod...
# Set up DNSsudo vim /etc/hosts # Add postgres.localsudo vim /etc/hosts # Add redis.localsudo vim /etc/hosts # Add myapp.local# Breaks after reboot, repeat...
# Install PostgreSQLhelm repo add bitnami https://...helm install postgres bitnami/postgresql \ --set auth.password=$(openssl rand -base64 32) \ --set primary.persistence.size=10Gi \ --set volumePermissions.enabled=true \ # 50 more lines...
# Oh, you changed one setting? Start over.You spend hours repeating commands, fighting drift, and maintaining state in your head.
The LoKO Way (Declarative Simplicity)
Section titled “The LoKO Way (Declarative Simplicity)”# loko.yamlname: my-projectcluster: workers: 2workloads: postgres: enabled: true storage: 10Giloko env create# Done. Everything configured.
# Need to change something?vim loko.yaml # Edit workers: 3, storage: 20Giloko config sync# Updated automatically.Your environment is code. Version it. Review it. Reproduce it.
Generating Configuration
Section titled “Generating Configuration”LoKO can auto-generate your configuration file with sensible defaults and auto-detected settings.
Quick Start
Section titled “Quick Start”loko config generateWhat it does:
- Auto-detects your local IP address by default
- Generates
loko.yamlwith sensible defaults - Adds helpful comments explaining each option
- Sets up proper network, cluster, and component configuration
- Includes a complete sample user workload that is present but disabled
If auto-detection is wrong or unavailable, you can override it:
loko config generate --local-ip 192.168.0.10Example output:
name: devnetwork: ip: 192.168.0.10 # Auto-detected or set via --local-ip domain: dev.me # Default domaincluster: workers: 2 # Sensible defaultgitops: enabled: false provider: fluxcd # or argocdworkloads: system: [] user: - name: http-webhook enabled: false namespace: default config: image: webhook.site/cli tag: latest port: 8084 replicas: 1Regenerating Configuration
Section titled “Regenerating Configuration”loko config generate --forceOverwrites your existing loko.yaml with fresh defaults and auto-detected IP.
Use when: You want to reset your config file or start fresh after changes.
Force Regeneration
Section titled “Force Regeneration”loko config generate --forceOverwrites existing loko.yaml (backup created automatically as loko.yaml.backup).
Use when: You want to reset to defaults or your config is corrupted.
Configuration Structure
Section titled “Configuration Structure”The loko.yaml file follows a structured schema with these main sections:
# Environment metadataname: string # Environment name (used for cluster naming)base-dir: string # Storage directory (default: .loko)expand-env-vars: boolean # Enable ${VAR} expansion
# Cluster configurationcluster: kubernetes: api-port: integer # Kubernetes API port (default: 6443) image: string # Kind node image (default: kindest/node) tag: string # Kubernetes version (e.g., v1.35.0) nodes: control-planes: integer # Control-plane count (always 1) workers: integer # Worker node count (default: 2)
# Network configurationnetwork: ip: string # Local IP address (auto-detected by default, or set via --local-ip) domain: string # Base domain (default: dev.me) dns-port: integer # dnsmasq host port (auto-selected from non-privileged ports (1024–32767), preferring 5453) lb-ports: [80, 443] # Load balancer ports
# Infrastructure componentscomponents: ingress-controller: # Traefik ingress version: string # Chart version dashboard: boolean # Enable Traefik dashboard at traefik.<domain> (default: false) registry: # Zot container registry version: string # Chart version metrics-server: # Kubernetes metrics (optional) enabled: boolean
# Workload configurationworkloads: use-presets: boolean # Use catalog presets (default: true) system: [] # System workloads (databases, caches, etc.) user: [] # User applications
# GitOps configurationgitops: enabled: boolean # Enable GitOps integration (default: false) provider: fluxcd|argocd # GitOps provider (default: fluxcd)Schema Sections Explained
Section titled “Schema Sections Explained”Environment:
name: Used for cluster naming (loko-{name})base-dir: Where LoKO stores state (default:.loko/)
Cluster:
kubernetes.tag: Kubernetes version to use (matches Kind node images)nodes.workers: Number of worker nodes (1-5 recommended for local dev)
Network:
network.ip: Auto-detected local IP (can override manually)network.domain: Base domain for all services (default:dev.me)network.dns-port: dnsmasq host port — auto-selected from non-privileged ports (1024–32767), preferring 5453, rarely needs manual configuration
Components:
ingress-controller: Traefik for ingress routingregistry: Zot registry for container imagesmetrics-server: Optional Kubernetes metrics API
Workloads:
system: Pre-configured catalog workloads (PostgreSQL, Redis, etc.)user: Your custom applications
Key Configuration Sections
Section titled “Key Configuration Sections”Environment Name
Section titled “Environment Name”name: dev-myprojectUsed for:
- Cluster name prefix
- Directory naming (
.loko/dev-myproject/) - Resource naming
Network Configuration
Section titled “Network Configuration”network: ip: 192.168.0.10 # Your local IP domain: mydev.local # Base domain # dns-port is auto-selected from non-privileged ports (1024–32767), preferring 5453Resulting DNS:
- System:
postgres.mydev.local - User:
myapp.mydev.local - Preview:
myapp-pr-1.pr.mydev.local - Registry:
cr.mydev.local
Cluster Configuration
Section titled “Cluster Configuration”cluster: kubernetes: image: kindest/node tag: v1.35.0 # Kubernetes version api-port: 6443
nodes: control-planes: 1 # Control-plane nodes (always 1) workers: 2 # Worker nodesNode Scheduling:
cluster: nodes: scheduling: control-plane: isolate-internal-components: true # Force Traefik/registry here # Note: allow-workloads and isolate-workloads are auto-configured based on worker countNode Labels:
cluster: nodes: labels: control-plane: tier: infrastructure worker: tier: applicationWorkload Configuration
Section titled “Workload Configuration”workloads: use-presets: true # Use catalog presets
system: - name: postgres enabled: true namespace: common-services # Preset values from catalog
- name: mysql enabled: false namespace: common-services storage: 10Gi
user: - name: myapp enabled: true namespace: apps config: chart: myrepo/myapp version: 1.0.0 values: ingress: enabled: true host: myapp.mydev.localRegistry Configuration
Section titled “Registry Configuration”registry: name: cr # Registry subdomain storage: 20Gi mirroring: enabled: true sources: - name: docker_hub enabled: true - name: quay enabled: true - name: ghcr enabled: trueConfiguration Validation
Section titled “Configuration Validation”LoKO validates your configuration before using it to prevent errors.
Validate Syntax
Section titled “Validate Syntax”loko config validateChecks for:
- ✅ YAML syntax errors
- ✅ Required fields present
- ✅ Type mismatches (e.g., string instead of integer)
- ✅ Invalid values (e.g., port out of range)
- ✅ Schema compliance
Example validation output:
✓ YAML syntax valid✓ All required fields present✓ Schema validation passed✓ Network configuration valid✓ Cluster configuration valid✓ Workload definitions valid
Configuration is valid ✓Check Port Availability
Section titled “Check Port Availability”loko check portsVerifies all configured ports are available before creating environment.
Checks:
- DNS port (default: 53)
- Load balancer ports (80, 443)
- Kubernetes API port (6443)
Sync Configuration
Section titled “Sync Configuration”# Dry-run (show changes)loko config sync
# Apply changesloko config syncWhat it does:
- Compares current
loko.yamlwith running environment - Shows what would change
- Applies safe updates to the running environment
Use when: You’ve modified loko.yaml and want to update the running environment without recreating it.
CLI Overrides
Section titled “CLI Overrides”Override any config value via CLI:
loko init \ --name my-cluster \ --workers 3 \ --local-ip 192.168.0.10 \ --local-domain mydev.local \ --registry-storage 50Gi \ --no-schedule-on-controlVersion Management
Section titled “Version Management”Renovate Comments
Section titled “Renovate Comments”Add renovate comments for automatic version upgrades:
cluster: kubernetes: image: kindest/node # renovate: datasource=docker depName=kindest/node tag: v1.35.0
components: ingress-controller: # renovate: datasource=helm depName=traefik repositoryUrl=https://traefik.github.io/charts version: "38.0.2"Upgrade Versions
Section titled “Upgrade Versions”loko config upgradeFetches latest versions and updates loko.yaml. Creates backup as loko-prev.yaml.
Environment Variables
Section titled “Environment Variables”Expansion
Section titled “Expansion”expand-env-vars: true
network: ip: ${MY_LOCAL_IP} # From environment domain: ${USER}.local # System variableLoko Variables
Section titled “Loko Variables”workloads: user: - name: myapp config: values: ingress: host: myapp.${LOKO_DOMAIN} # Auto-providedAvailable: LOKO_DOMAIN, LOKO_IP, LOKO_ENV_NAME
Multiple Environments
Section titled “Multiple Environments”Separate Config Files
Section titled “Separate Config Files”Create each environment from its own config file, then activate the one you want to work with.
# Create configsloko config generate --name dev-project-a -o loko-project-a.yaml --local-ip 192.168.0.10loko config generate --name dev-project-b -o loko-project-b.yaml --local-ip 192.168.0.10
# Create both environmentsloko env create -c loko-project-a.yamlloko env create -c loko-project-b.yaml
# Activate one as currentloko activate dev-project-aloko status
# Override explicitly when neededloko status -c loko-project-b.yamlEnvironment Profiles
Section titled “Environment Profiles”# loko-dev.yamlname: devcluster: nodes: workers: 2
# loko-prod-like.yamlname: prod-likecluster: nodes: workers: 5Real Developer Workflows
Section titled “Real Developer Workflows”Full-Stack Developer: API + Database + Frontend
Section titled “Full-Stack Developer: API + Database + Frontend”# loko.yamlname: my-saas-appcluster: workers: 2
workloads: postgres: enabled: true storage: 10Gi
valkey: enabled: true # Redis-compatible cacheWorkflow:
# Create environmentloko env create
# Build and deploy APIdocker build -t cr.dev.me/api:latest ./apidocker push cr.dev.me/api:latestkubectl apply -f k8s/api/
# Build and deploy frontenddocker build -t cr.dev.me/frontend:latest ./frontenddocker push cr.dev.me/frontend:latestkubectl apply -f k8s/frontend/
# Access:# - API: https://api.dev.me# - Frontend: https://app.dev.me# - Database: psql -h postgres.dev.me -U postgres# - Cache: redis-cli -h valkey.dev.mePlatform Engineer: Testing Helm Charts
Section titled “Platform Engineer: Testing Helm Charts”# loko.yamlname: chart-testingcluster: workers: 3 # Test pod affinity/anti-affinity
workloads: postgres: enabled: true rabbitmq: enabled: trueWorkflow:
# Create multi-node clusterloko env create
# Test your Helm charthelm install myapp ./charts/myapp --dry-runhelm install myapp ./charts/myapp
# Test upgradeshelm upgrade myapp ./charts/myapp
# Verify multi-node behaviorkubectl get pods -o wide # Check pod distributionBackend Developer: Microservices Development
Section titled “Backend Developer: Microservices Development”# loko.yamlname: microservicescluster: workers: 2
workloads: postgres: enabled: true rabbitmq: enabled: true valkey: enabled: true garage: enabled: true # S3-compatible storageWorkflow:
# Create environment with all servicesloko env create
# Each microservice can now use:# - postgres.dev.me:5432 (database)# - rabbitmq.dev.me:5672 (messaging)# - valkey.dev.me:6379 (cache)# - garage.dev.me (S3 storage)
# Develop and test locallydocker-compose up # Or deploy to clusterConfiguration Patterns
Section titled “Configuration Patterns”Development Environment
Section titled “Development Environment”Fast iteration, minimal resources:
name: devcluster: workers: 1 # Single worker for speed
workloads: postgres: enabled: true storage: 5Gi # Smaller storage
valkey: enabled: trueStaging/Production-Like Environment
Section titled “Staging/Production-Like Environment”Test at scale, match production:
name: stagingcluster: workers: 5 # Match production node count
workloads: postgres: enabled: true storage: 50Gi # Production-like storage config: replicas: 3 # HA configuration
valkey: enabled: true config: replicas: 3Multi-Project Setup
Section titled “Multi-Project Setup”Isolate different projects:
# Create separate configscat > loko-project-a.yaml <<EOFname: project-acluster: workers: 2workloads: postgres: enabled: true rabbitmq: enabled: trueEOF
cat > loko-project-b.yaml <<EOFname: project-bcluster: workers: 1workloads: mongodb: enabled: true nats: enabled: trueEOF
# Create themloko env create -c loko-project-a.yamlloko env create -c loko-project-b.yaml
# Activate the one you want to work onloko activate project-aloko status
# Or target the other config directlyloko status -c loko-project-b.yamlBest Practices
Section titled “Best Practices”- Version control
loko.yaml- Share configs with your team - Use renovate comments - Keep versions up-to-date automatically
- Document custom settings - Future you will thank you
- Use catalog presets - Don’t reinvent PostgreSQL configuration
- Test with
config sync- Preview changes before applying - Start small, scale up - Begin with 1 worker, add more as needed
❌ Don’t
Section titled “❌ Don’t”- Hardcode IPs - Use auto-detection for portability
- Store secrets - LoKO auto-generates and saves them separately
- Use port 53 - Change to 5353 if conflict with systemd-resolved
- Modify generated configs - Edit
loko.yamlinstead (they regenerate) - Over-provision - 2-3 workers is plenty for local development
Troubleshooting
Section titled “Troubleshooting”Config Syntax Error
Section titled “Config Syntax Error”# Validate YAMLloko config validate
# Check specific sectioncat loko.yaml | yq '.cluster'DNS Not Resolving
Section titled “DNS Not Resolving”# Check dnsmasq podskubectl get pods -n kube-system -l k8s-app=kube-dnskubectl get pods -n loko-components -l app.kubernetes.io/name=dnsmasq
# Recreate DNSloko dns recreateWorkload Not Deploying
Section titled “Workload Not Deploying”# Check workload is enabledyq '.workloads.system[] | select(.name == "postgres")' loko.yaml
# Enable itloko workloads enable postgresExamples
Section titled “Examples”See loko.yaml.comprehensive.example in the repository for a complete example with all options.
Next Steps
Section titled “Next Steps”- Environment Lifecycle - Manage environments
- Workload Management - Deploy services
- Config Schema Reference - Complete schema