A Python CLI utility to manage local Kubernetes environments with Kind, providing simplified configuration management, version upgrades, DNS, wildcard certificates, local container registry, and extensive customization options.
- Easy Setup: Initialize local Kubernetes clusters with a single command
- Smart Version Management: Upgrade component versions using loko-updater comments
- Automatic Backups: Config files are automatically backed up before upgrades
- Custom Templates: Use your own Jinja2 templates for configuration generation
- Extensive CLI Overrides: Override any configuration value via command-line flags
- Built-in Local Registry: Local container registry with TLS support
- Automatic HTTPS: Built-in certificate management with mkcert
- Local DNS: Automatic DNS configuration for local development
- Metrics & Monitoring: Built-in metrics-server for resource monitoring and HPA support
- Comprehensive Status: Detailed view of cluster resources with
loko status - Granular Workload Management: List, deploy, and undeploy individual workloads with
loko workload - Advanced Node Scheduling: Flexible node labeling and workload placement
- Registry Mirroring: Automatic caching/mirroring of external registries (Docker Hub, Quay, etc.)
- Workload Presets: Pre-configured settings for common workloads (MySQL, PostgreSQL, Valkey, etc.)
- Helm-based Deployment: Deploy workloads from public repositories (groundhog2k, etc.)
- Centralized Helm Repos: Define repositories once, reference everywhere
- Automatic Secrets Management: Automatically generate, fetch and save workload credentials with deduplication
- Port Availability Checking: Pre-flight validation ensures all required ports are available before cluster creation
- Smart Error Handling: Clear, actionable error messages guide you to solutions
Important for existing users: The configuration schema has been restructured for better organization and clarity. If you have an existing
loko.yaml, you'll need to regenerate it.
-
Backup your existing config (if you have customizations):
cp loko.yaml loko.yaml.backup
-
Regenerate the config:
loko config generate --force
-
Re-apply your customizations to the new config structure.
| Old Path | New Path |
|---|---|
local-ip |
network.ip |
local-domain |
network.domain |
local-dns-port |
network.dns-port |
local-lb-ports |
network.lb-ports |
use-apps-subdomain |
network.subdomain.enabled |
apps-subdomain |
network.subdomain.value |
provider |
cluster.provider |
kubernetes |
cluster.kubernetes |
nodes |
cluster.nodes |
nodes.allow-scheduling-on-control-plane |
cluster.nodes.scheduling.control-plane.allow-workloads |
nodes.internal-components-on-control-plane |
cluster.nodes.scheduling.control-plane.isolate-internal-components |
run-workloads-on-workers-only |
cluster.nodes.scheduling.workers.isolate-workloads |
internal-components (list) |
internal-components (dict with named components) |
helm-repositories |
workloads.helm-repositories |
- Python 3.9 or higher
- Docker
- Kind
- mkcert (for HTTPS certificates)
- Helm
- Helmfile
- (optional) nss (for macOS) or libnss3-tools (for Linux) - needed for Firefox to trust mkcert certificates
Mise is the recommended way to install and manage CLI tools. Clone the repo and run mise install from the project root to install all prerequisites:
git clone https://github.com/bojanraic/loko.git
cd loko
mise install # Installs kind, helm, kubectl, mkcert, helmfileUsing pip:
pip install loko-k8sUsing uv:
uv tool install loko-k8sgit clone https://github.com/bojanraic/loko.git
cd loko
pip install -e .git clone https://github.com/bojanraic/loko.git
cd loko
uv sync
uv run loko --help-
Check prerequisites:
loko check-prerequisites
-
Generate a default configuration:
loko config generate # Or use --minimal for a compact config without comments/disabled sections: # loko config generate --minimal
-
Initialize your environment:
loko init
-
Create the full environment:
loko create
Watch Loko in action - see the complete workflow from installation to cluster validation:
Demo highlights:
- Installing loko
- Generating a default configuration with auto-detected IP
- Creating a local Kubernetes cluster with Kind
- Deploying workloads (Traefik, container registry, PostgreSQL) with Helm
- Validating the cluster setup
- Checking environment status
- Upgrading component versions
- Viewing workload secrets & connecting to PostgreSQL and test application
- Stopping and starting environment
- Installing shell completion (via
loko completion <shell>)
loko init- Initialize environment (generate configs, setup certs, network)loko create- Create full environment with complete workflowloko start- Start all cluster containersloko stop- Stop all cluster containersloko destroy- Destroy the environmentloko recreate- Destroy and recreate the environmentloko clean- Destroy environment and remove all artifacts
loko status- Show comprehensive environment statusloko validate- Validate the environmentloko check-prerequisites- Check if required tools are installedloko completion <shell>- Generate shell completion script (bash, zsh, fish)
loko config generate- Generate default loko.yaml with auto-detected local IP (use--minimalfor compact config without comments)loko config compact- Compact existing config by removing comments and disabled sectionsloko config detect-ip- Detect and display the local IP addressloko config validate- Validate configuration file structure and valuesloko config port-check- Check availability of all configured portsloko config dns-check- Check DNS configuration and resolution statusloko config upgrade- Upgrade component versions using loko-updater commentsloko config helm-repo-add- Add Helm repositories to configloko config helm-repo-remove- Remove Helm repositories from configloko workload list- List workloads and their status (with filtering options)loko workload deploy- Deploy all or specific workloadsloko workload undeploy- Undeploy all or specific workloadsloko secret fetch- Fetch workload credentials from clusterloko secret show- Display saved workload credentialsloko registry status- Show registry statistics and configurationloko registry list-repos- List all repositories in the registryloko registry show-repo <name>- Show details about a specific repositoryloko registry list-tags <name>- List all tags for a repository
Use the status command to get a comprehensive overview of your local Kubernetes environment:
loko statusThis will display:
- Cluster Status: Overall health of the Kubernetes cluster
- Container Status: Status of all related containers (nodes, DNS, etc.)
- Node Status: List of all nodes with their roles and status
- DNS Status: Status of the local DNS service
Migration Note: If you're upgrading from an earlier version of Loko that used
# renovate:comments, you'll need to update them to# loko-updater:. A simple find-and-replace in yourloko.yamlwill do:sed -i '' 's/# renovate:/# loko-updater:/g' loko.yaml # macOS sed -i 's/# renovate:/# loko-updater:/g' loko.yaml # Linux
Loko uses loko-updater comments in your configuration file to track and upgrade component versions. This approach allows you to:
- Keep component versions up-to-date
- Track version sources directly in your config
- Automatically query Docker Hub and Helm repositories for latest versions
Add loko-updater comments above the version fields in your loko.yaml:
cluster:
kubernetes:
image: kindest/node
# loko-updater: datasource=docker depName=kindest/node
tag: v1.35.0
internal-components:
traefik:
# loko-updater: datasource=helm depName=traefik repositoryUrl=https://traefik.github.io/charts
version: "38.0.2"
workloads:
system:
- name: mysql
config:
chart: groundhog2k/mysql
# loko-updater: datasource=helm depName=mysql repositoryUrl=https://groundhog2k.github.io/helm-charts
version: 3.0.8- Docker Hub (
datasource=docker): Fetches latest tags from Docker Hub - Helm Repositories (
datasource=helm): Fetches latest chart versions from Helm repos
loko config upgradeThis will:
- Scan your config for loko-updater comments
- Query each datasource for the latest version (in parallel)
- Create a backup (
loko-prev.yaml) - Update versions in place
- Show a summary of changes
Performance: Helm repository checks are performed in parallel, significantly reducing upgrade time when checking multiple repositories.
Example output:
Upgrading component versions...
Updates found:
kindest/node: v1.34.0 → v1.35.0
traefik: 37.3.0 → 38.0.2
mysql: 3.0.7 → 3.0.8
Backup created: loko-prev.yaml
Updated 3 version(s) in loko.yaml
Total fetch time: 8.18s (Helm ops: 8.18s)
If an upgrade causes issues, easily revert:
mv loko-prev.yaml loko.yamlLoko provides commands to manage Helm repositories in your configuration file:
# Add a single repository
loko config helm-repo-add \
--helm-repo-name bitnami \
--helm-repo-url https://charts.bitnami.com/bitnami
# Add multiple repositories at once
loko config helm-repo-add \
--helm-repo-name bitnami --helm-repo-url https://charts.bitnami.com/bitnami \
--helm-repo-name jetstack --helm-repo-url https://charts.jetstack.io# Remove a single repository
loko config helm-repo-remove --helm-repo-name bitnami
# Remove multiple repositories
loko config helm-repo-remove \
--helm-repo-name bitnami \
--helm-repo-name jetstackAdded repositories appear in your config and can be referenced:
environment:
workloads:
helm-repositories:
- name: bitnami
url: https://charts.bitnami.com/bitnami/
user:
- name: my-app
config:
repo:
ref: bitnami # Reference the added repository
chart: bitnami/nginx
version: 1.0.0Workload credentials (database passwords, etc.) are automatically generated during deployment:
# Fetch credentials from the cluster
loko secret fetch
# Display saved credentials
loko secret showTip:
loko secret showwill automatically trigger afetchif the secrets file doesn't exist yet.
Credentials are saved locally to:
<base-dir>/<env-name>/workload-secrets.txt
Example workloads with auto-generated credentials:
- MySQL (root password)
- PostgreSQL (postgres password)
- MongoDB (root password)
- RabbitMQ (admin password)
- Valkey (default password)
When you run loko init or loko create, a .loko directory is created (configurable via base-dir).
.
├── loko.yaml # Main configuration file
├── .loko/ # Default directory for cluster data and configs
│ └── <env-name>/ # Environment-specific directory (e.g. dev-me)
│ ├── certs/ # TLS certificates and keys
│ │ ├── rootCA.pem # Root CA certificate
│ │ ├── <domain>.pem # Domain certificate
│ │ ├── <domain>-key.pem # Domain private key
│ │ └── <domain>-combined.pem # Combined cert and key
│ ├── config/ # Generated configuration files
│ │ ├── cluster.yaml # KinD cluster configuration
│ │ ├── containerd/ # Container runtime config (per registry)
│ │ │ ├── cr.dev.me/hosts.toml
│ │ │ └── docker.io/hosts.toml
│ │ ├── dnsmasq.conf # Local DNS configuration
│ │ └── helmfile.yaml # Helm releases definition
│ ├── logs/ # Kubernetes node logs
│ ├── storage/ # Persistent volume data
│ ├── kubeconfig # Cluster access configuration
│ └── workload-secrets.txt # Generated workload credentials
Note: The
.lokodirectory is git-ignored by default.
Loko permits granular control over your workloads through the workload command group.
View all enabled workloads, their type, namespace, and current status:
loko workload listFilter by type or status:
loko workload list --all # All workloads including disabled
loko workload list --user # Only enabled user workloads
loko workload list --system # Only enabled system workloads
loko workload list --internal # Only enabled internal components (Traefik, Registry, etc.)
loko workload list --disabled # Only disabled workloads
loko workload list --system --disabled # Disabled system workloads onlyDeploy or undeploy specific workloads:
# Deploy all user and system workloads
loko workload deploy
# Deploy a specific workload
loko workload deploy mongodb
# Undeploy a specific workload
loko workload undeploy garage
# Include internal workloads
loko workload deploy --internalThe deploy and undeploy commands default to targeting user and system workloads. Use --all or specific type flags to include internal components.
Note: Selective deployment uses
helmfile --selectorunder the hood.
-
System Workloads (
workload.network.domain):- Core infrastructure workloads (databases, message queues, etc.)
- Direct DNS resolution (e.g.,
mysql.dev.me,postgres.dev.me)
-
User Workloads (
workload.network.subdomain.value.network.domain):- Custom applications and workloads
- Either under subdomain (
myapp.apps.dev.me) or direct domain (myapp.dev.me) - Configurable via
network.subdomain.enabledsetting
-
Internal Components:
- Registry (e.g.,
cr.dev.me) - Traefik ingress controller
- DNS service (dnsmasq)
- Metrics server (optional)
- Registry (e.g.,
Once the environment is running, workloads are accessible through:
-
Direct Port Access:
# Example for PostgreSQL psql -h localhost -p 5432 -U postgres -
Domain Names:
# Example for system workload psql -h postgres.dev.me -U postgres -
Workload Credentials:
- Passwords are automatically generated and stored in
<base-dir>/<env-name>/workload-secrets.txt - Or fetch them with:
loko secret show
- Passwords are automatically generated and stored in
The environment includes a local container registry accessible at <registry.name>.<network.domain>.
-
Push Images:
docker tag myapp:latest cr.dev.me/myapp:latest docker push cr.dev.me/myapp:latest
-
Use in Kubernetes:
image: cr.dev.me/myapp:latest
The environment supports advanced node scheduling configurations to separate infrastructure and application workloads.
Configure custom labels in loko.yaml:
cluster:
nodes:
labels:
control-plane:
tier: "infrastructure"
worker:
tier: "application"Control workload placement with the scheduling section:
cluster:
nodes:
scheduling:
control-plane:
allow-workloads: true # Allow user/system workloads on control-plane
isolate-internal-components: true # Force Traefik/registry to control-plane only
workers:
isolate-workloads: true # Force user/system workloads to workers onlyLoko includes validation workflows for testing OCI registry functionality.
Run validation with:
loko validateThis runs a comprehensive check including:
- Cluster status and node readiness
- DNS service health
- System pods status
- Kubectl connectivity
- Registry & TLS Validation: Builds a test image, pushes to local registry, deploys a test app, and verifies connectivity.
Use the registry command group to inspect and manage the local container registry:
# Show registry statistics and configuration
loko registry status
# List all repositories (local and mirrored)
loko registry list-repos
# Show details about a specific repository
loko registry show-repo myapp
loko registry show-repo docker.io/library/nginx
# List all tags for a repository
loko registry list-tags myappWhen mirroring is enabled, images pulled through the cluster are cached in the local registry. Use list-repos to verify mirroring is working.
The environment is configured through loko.yaml. Generate a default one with loko config generate.
environment:
# General settings
name: string # Name of the environment
base-dir: string # Base directory for storage
expand-env-vars: boolean # Whether to expand OS and loko variables
# Cluster configuration
cluster:
provider:
name: string # Provider name (currently only "kind" supported)
runtime: string # Container runtime (docker or podman)
kubernetes:
api-port: integer # API server port
image: string # Node image
tag: string # Node image tag
nodes:
servers: integer # Number of control-plane nodes
workers: integer # Number of worker nodes
scheduling:
control-plane:
allow-workloads: boolean
isolate-internal-components: boolean
workers:
isolate-workloads: boolean
labels: # Optional custom node labels
control-plane: {}
worker: {}
# Network configuration
network:
ip: string # Local IP for DNS resolution
domain: string # Domain name
dns-port: integer # DNS resolver port (default 53)
subdomain:
enabled: boolean # Use subdomain for user apps
value: string # Subdomain value (e.g., "apps")
lb-ports: array # Load balancer ports [80, 443]
# Registry configuration
registry:
name: string # Registry name (e.g., "cr")
storage:
size: string # PVC size (e.g., "10Gi")
mirroring:
enabled: boolean
sources: array # List of mirror sources
# Internal components (infrastructure)
internal-components:
traefik:
version: string
zot:
version: string
dnsmasq:
version: string
metrics-server:
version: string
enabled: boolean # Only metrics-server is optional
# Workload configuration
workloads:
use-presets: boolean # Whether to use workload presets
helm-repositories: array # Centralized Helm repo definitions
system: array # List of system workloads
user: array # List of user-defined workloadsLoko provides extensive CLI options to override almost any configuration value during initialization:
loko init --name my-cluster --workers 3 --registry-storage 50Gi --no-schedule-on-controlSee loko init --help for all available overrides.
Loko runs a lightweight DNS container on your machine so that services such as postgres.dev.me resolve locally. By default it binds to the standard DNS port 53, but some hosts already listen on that port. If you need to avoid a conflict, set network.dns-port in loko.yaml (or pass --dns-port to loko init). Loko will start the DNS container on the specified port and automatically update the /etc/resolver/<domain> entry so lookups continue to work.
-
DNS Resolution Issues
- Run DNS diagnostics:
loko config dns-check - Verify local DNS container is running:
loko status - Check DNS configuration:
cat /etc/resolver/<your-domain>
- Run DNS diagnostics:
-
Certificate Issues
- Regenerate certificates:
loko init(will re-run certificate setup) - Verify cert location:
ls <base-dir>/<env-name>/certs/
- Regenerate certificates:
-
Workload Access Issues
- Validate environment:
loko validate - Verify ingress:
kubectl get ingress -A - Check credentials:
loko secret show
- Validate environment:
-
OCI Registry Issues
- Test registry connectivity:
docker pull cr.dev.me/test:latest - Run validation:
loko validate
- Test registry connectivity:
-
Version Upgrade Issues
- Restore from backup:
mv loko-prev.yaml loko.yaml - Check loko-updater comment syntax in config file
- Restore from backup:
-
Port Conflicts
- Check what's using a port:
sudo lsof -i :53(macOS) orsudo netstat -tlnp | grep :53(Linux) - Change the DNS port in config:
network.dns-port: 5353
- Check what's using a port:
git clone https://github.com/bojanraic/loko.git
cd loko
uv sync
uv run loko --help# Run all unit tests
uv run pytest tests/ --ignore=tests/integration
# Run with verbose output
uv run pytest tests/ -v --ignore=tests/integrationuv run ruff check loko/
uv run ruff format loko/This project is licensed under the MIT License - see the LICENSE file for details.