kindling is a development engine that starts by wiring up your CI pipeline — fast, free, and local — then keeps working for you long after the first deploy. The outer loop runs real CI on your laptop via a local Kind cluster: push code, build containers, deploy staging environments. The inner loop skips all of that: edit a file, sync it into the running container, see the result instantly. A built-in web dashboard ties it all together.
Start a project with kindling and get CI that works in minutes. Stay for live sync, auto-provisioned dependencies, secrets management, public tunnels, and a visual dashboard — the dev environment that grows with you.
Every git push triggers a real GitHub Actions workflow. But instead of running on GitHub's cloud runners, the job is routed back to a self-hosted runner inside a Kind cluster on your machine. Kaniko builds the container, pushes it to an in-cluster registry, and the kindling operator deploys a complete staging environment — Deployment, Service, Ingress, and up to 15 types of auto-provisioned dependencies.
This is your CI/CD pipeline, but it's free, instant, and local.
kindling init # bootstrap cluster + operator
kindling runners -u <user> -r <org/repo> -t <pat> # register runner with GitHub
kindling generate -k <api-key> -r /path/to/app # AI-generate workflow
git push # triggers build + deployOnce your app is deployed, you don't need to push code to iterate. kindling sync watches your local files, copies changes directly into the running container, and restarts the process using the right strategy for the language — all in under a second.
kindling sync -d my-api --restart # watch + auto-restartThe runtime is auto-detected from the container's process:
| Strategy | Runtimes | What happens |
|---|---|---|
| Signal reload | uvicorn, gunicorn, Puma, Nginx, Apache | SIGHUP for zero-downtime reload |
| Wrapper + kill | Node.js, Python, Ruby, Perl, Elixir, Deno, Bun | Restart-loop wrapper respawns process with new code |
| Local build + binary sync | Go, Rust, Java, Kotlin, C#, C/C++, Zig | Cross-compiles locally for container arch, syncs binary |
| Frontend build + deploy | React, Vue, Angular, Svelte (nginx) | Builds locally, syncs dist output into nginx html root |
| Auto-reload | PHP, nodemon | Just syncs files — runtime picks them up |
When you stop syncing, the deployment automatically rolls back to its original state — no manual cleanup, no stale wrapper processes.
kindling dashboard launches a web UI at localhost:9090 that shows your entire cluster at a glance: environments, pods, services, ingresses, logs, and events. Every deployed service gets Sync and Load buttons for one-click inner-loop iteration:
- Sync — Detects the runtime, starts live file sync with the right restart strategy, shows sync count and status in real time
- Load — Rebuilds the container image locally via
docker build, loads it into Kind, and triggers a rolling update - Runtime badges — Each service shows its detected runtime (Node.js, Python, Go, etc.) so you know exactly what restart strategy will be used
kindling dashboard # open at localhost:9090
kindling dashboard --port 8080 # custom port# 1. Bootstrap the local cluster
kindling init
# 2. Register a GitHub Actions runner
kindling runners -u alice -r acme/myapp -t ghp_xxxxx
# 3. AI-generate a workflow for your app
kindling generate -k sk-... -r /path/to/myapp
# 4. Push code → outer loop runs automatically
git push origin main
# 5. Open the dashboard to see your environments
kindling dashboard
# 6. Start the inner loop — live sync a service
kindling sync -d alice-myapp --restart
# 7. Edit code locally → changes appear instantly
# Stop sync (Ctrl+C) → deployment rolls back automaticallyOuter loop: git push → GitHub dispatches job → self-hosted runner builds via Kaniko → operator deploys staging environment → accessible at localhost.
Inner loop: Edit a file → kindling sync copies it into the container → auto-detected restart strategy → see changes instantly → stop sync → deployment rolls back.
Dashboard: Visual interface for both loops — view environments, trigger sync/load, monitor pod health, tail logs.
kindling sync is the engine of the inner loop. It auto-detects the runtime from the container's PID 1 command line and chooses the optimal restart strategy from 30+ known process signatures.
For Go, Rust, Java, and other compiled languages, kindling sync queries the Kind node's architecture and cross-compiles locally:
# Go — auto cross-compiles for container arch
kindling sync -d my-gateway --restart --language go
# Custom build command
kindling sync -d my-gateway --restart \
--build-cmd 'CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -o ./bin/gw .' \
--build-output ./bin/gwFor Node.js, Python, Ruby, etc., files are synced directly and the process is restarted via a wrapper loop:
# Node.js service — auto-detected
kindling sync -d my-api --restart
# Python/uvicorn — detects SIGHUP strategy
kindling sync -d orders --src ./services/orders --restartReact, Vue, Angular, and other frontend projects that build to static assets served by nginx:
# Builds locally, syncs dist/ into nginx html root
kindling sync -d my-ui --restartWhen you stop a sync session (Ctrl+C or via the dashboard), kindling automatically restores the deployment to its pre-sync state:
- If the deployment was patched (wrapper injected), it performs a
rollout undoto the saved revision - If only files were synced (signal-reload servers), it performs a
rollout restartfor a fresh pod - Either way, the container returns to exactly the state it was in before sync started
| Flag | Short | Default | Description |
|---|---|---|---|
--deployment |
-d |
— (required) | Target deployment name |
--src |
— | . |
Local source directory to watch |
--dest |
— | /app |
Destination path inside the container |
--restart |
— | false |
Restart process after each sync |
--once |
— | false |
Sync once and exit (no file watching) |
--language |
— | auto-detect | Override runtime detection |
--build-cmd |
— | auto-detect | Build command for compiled languages |
--build-output |
— | auto-detect | Path to built artifact |
--exclude |
— | — | Additional exclude patterns (repeatable) |
--debounce |
— | 500ms |
Debounce interval for batching changes |
--container |
— | — | Container name (multi-container pods) |
--namespace |
-n |
default |
Kubernetes namespace |
→ CLI Reference — kindling sync
The kindling dashboard is a React/TypeScript single-page app embedded in the CLI binary. It provides a complete visual interface for managing your local development cluster.
| Feature | Description |
|---|---|
| Environments | View all DevStagingEnvironments with status, image, replicas |
| Sync | One-click live sync with runtime detection and status tracking |
| Load | Rebuild + load container images without pushing to GitHub |
| Pods | View pod status, restart counts, container readiness |
| Logs | Tail container logs in real time |
| Services | View all services with ports and selectors |
| Ingresses | View routing rules and hostnames |
| Events | Kubernetes events stream for debugging |
| Secrets | Create and manage kindling secrets |
| Runners | View and create GitHub Actions runner pools |
| Deploy | Apply DevStagingEnvironment YAML directly |
| Env Vars | Set/unset environment variables on deployments |
| Scale | Scale deployments up or down |
| Restart | Rolling restart deployments |
| Expose | Start/stop public HTTPS tunnels |
| Destroy | Tear down the cluster |
Each service in the dashboard shows a badge indicating its detected runtime (e.g., Node.js, Python/uvicorn, Go, nginx). This tells you exactly what restart strategy kindling sync will use, before you click the Sync button.
kindling dashboardThe operator manages two CRDs in the apps.example.com/v1alpha1 group:
Declares a self-hosted runner pool bound to a developer and a GitHub repository. The operator creates a Deployment with two containers:
- Runner — the official GitHub Actions runner, registered with your PAT
- Build-agent sidecar — watches
/buildsfor build and deploy requests, runs Kaniko pods andkubectl apply
apiVersion: apps.example.com/v1alpha1
kind: GithubActionRunnerPool
metadata:
name: jeff-runner-pool
spec:
githubUsername: "jeff-vincent"
repository: "jeff-vincent/demo-kindling"
tokenSecretRef:
name: github-runner-token
replicas: 1Full GithubActionRunnerPool spec reference
| Field | Default | Description |
|---|---|---|
githubUsername |
(required) | Developer's GitHub handle — auto-added as a runner label |
repository |
(required) | GitHub repo slug (org/repo) |
tokenSecretRef |
(required) | Reference to a Secret holding a GitHub PAT (repo scope) |
replicas |
1 |
Number of runner pods |
runnerImage |
ghcr.io/actions/actions-runner:latest |
Runner container image |
labels |
[] |
Extra runner labels (self-hosted + username always added) |
runnerGroup |
"Default" |
GitHub runner group |
resources |
nil |
CPU/memory requests and limits |
serviceAccountName |
"" |
SA for the runner pod (auto-created if empty) |
workDir |
/home/runner/_work |
Runner working directory |
githubURL |
https://github.com |
Override for GitHub Enterprise Server |
env |
[] |
Extra environment variables |
volumeMounts |
[] |
Additional volume mounts |
volumes |
[] |
Additional volumes |
Declares a complete ephemeral staging environment: a Deployment, a Service, an optional Ingress, and zero or more Dependencies. The operator auto-provisions each dependency and injects connection env vars into the app container.
apiVersion: apps.example.com/v1alpha1
kind: DevStagingEnvironment
metadata:
name: jeff-dev
spec:
deployment:
image: registry:5000/myapp:jeff-abc123
replicas: 1
port: 8080
healthCheck:
path: /healthz
service:
port: 8080
ingress:
enabled: true
host: jeff-dev.localhost
ingressClassName: nginx
dependencies:
- type: postgres
version: "16"
- type: redisSupported dependency types (15)
| Type | Default Image | Port | Injected Env Var | Notes |
|---|---|---|---|---|
postgres |
postgres:16 |
5432 | DATABASE_URL |
Auto-creates devdb with user devuser |
redis |
redis:latest |
6379 | REDIS_URL |
Stateless, no persistence |
mysql |
mysql:latest |
3306 | DATABASE_URL |
Auto-creates devdb with user devuser |
mongodb |
mongo:latest |
27017 | MONGO_URL |
Root user devuser |
rabbitmq |
rabbitmq:3-management |
5672 | AMQP_URL |
Includes management UI |
minio |
minio/minio:latest |
9000 | S3_ENDPOINT |
Also injects S3_ACCESS_KEY + S3_SECRET_KEY |
elasticsearch |
docker.elastic.co/.../elasticsearch |
9200 | ELASTICSEARCH_URL |
Single-node, security disabled |
kafka |
apache/kafka:latest |
9092 | KAFKA_BROKER_URL |
KRaft mode (no ZooKeeper) |
nats |
nats:latest |
4222 | NATS_URL |
Lightweight messaging |
memcached |
memcached:latest |
11211 | MEMCACHED_URL |
In-memory cache |
cassandra |
cassandra:latest |
9042 | CASSANDRA_URL |
Single-node dev cluster |
consul |
hashicorp/consul:latest |
8500 | CONSUL_HTTP_ADDR |
Service mesh / KV store |
vault |
hashicorp/vault:latest |
8200 | VAULT_ADDR |
Dev mode, also injects VAULT_TOKEN |
influxdb |
influxdb:latest |
8086 | INFLUXDB_URL |
Time-series database |
jaeger |
jaegertracing/all-in-one:latest |
16686 | JAEGER_ENDPOINT |
Distributed tracing UI |
Full DevStagingEnvironment spec reference
| Field | Default | Description |
|---|---|---|
deployment.image |
(required) | Container image to run |
deployment.port |
(required) | Container port |
deployment.replicas |
1 |
Pod replica count |
deployment.command |
[] |
Override container entrypoint |
deployment.args |
[] |
Entrypoint arguments |
deployment.env |
[] |
Environment variables |
deployment.resources |
nil |
CPU/memory requests and limits |
deployment.healthCheck.path |
/healthz |
HTTP health check path |
deployment.healthCheck.initialDelaySeconds |
5 |
Delay before first probe |
deployment.healthCheck.periodSeconds |
10 |
Probe interval |
service.port |
(required) | Service port |
service.targetPort |
container port | Target port on the pod |
service.type |
ClusterIP |
ClusterIP / NodePort / LoadBalancer |
ingress.enabled |
false |
Create an Ingress resource |
ingress.host |
"" |
Hostname for the Ingress rule |
ingress.path |
/ |
URL path prefix |
ingress.pathType |
Prefix |
Prefix / Exact / ImplementationSpecific |
ingress.ingressClassName |
nil |
Ingress class (e.g. nginx) |
ingress.tls |
nil |
TLS termination config |
ingress.annotations |
{} |
Extra Ingress annotations |
dependencies[].type |
(required) | One of the 15 supported types above |
dependencies[].version |
latest | Image tag (e.g. "16", "7.2") |
dependencies[].image |
per-type default | Override the container image entirely |
dependencies[].port |
per-type default | Override the service port |
dependencies[].env |
[] |
Extra/override env vars for the dependency container |
dependencies[].envVarName |
per-type default | Override the injected env var name |
dependencies[].storageSize |
1Gi |
PVC size for stateful dependencies |
dependencies[].resources |
nil |
CPU/memory requests and limits |
Two composite actions eliminate workflow boilerplate:
Builds a container image via the Kaniko sidecar:
- uses: kindling-sh/kindling/.github/actions/kindling-build@main
with:
name: my-app
context: ${{ github.workspace }}
image: "registry:5000/my-app:${{ env.TAG }}"
⚠️ Dockerfile required:kindling-buildruns your Dockerfile as-is via Kaniko. Each service must have a working Dockerfile.
Generates and applies a DevStagingEnvironment CR:
- uses: kindling-sh/kindling/.github/actions/kindling-deploy@main
with:
name: "${{ github.actor }}-my-app"
image: "registry:5000/my-app:${{ env.TAG }}"
port: "8080"
ingress-host: "${{ github.actor }}-my-app.localhost"
dependencies: |
- type: postgres
version: "16"Full kindling-deploy input reference
| Input | Required | Default | Description |
|---|---|---|---|
name |
✅ | DSE metadata.name | |
image |
✅ | Container image reference | |
port |
✅ | Container port | |
labels |
"" |
Extra labels (YAML block) | |
env |
"" |
Extra env vars (YAML block) | |
dependencies |
"" |
Dependencies (YAML block) | |
ingress-host |
"" |
Ingress hostname | |
ingress-class |
nginx |
Ingress class name | |
health-check-path |
/healthz |
HTTP health check path | |
replicas |
1 |
Pod replica count | |
service-type |
ClusterIP |
Service type | |
wait |
true |
Wait for deployment rollout | |
wait-timeout |
180s |
Rollout timeout |
Download from GitHub Releases:
# macOS (Apple Silicon)
curl -Lo kindling.tar.gz https://github.com/kindling-sh/kindling/releases/latest/download/kindling_$(curl -s https://api.github.com/repos/kindling-sh/kindling/releases/latest | grep tag_name | cut -d '"' -f4 | sed 's/^v//')_darwin_arm64.tar.gz
tar xzf kindling.tar.gz
sudo mv kindling /usr/local/bin/
# macOS (Intel)
curl -Lo kindling.tar.gz https://github.com/kindling-sh/kindling/releases/latest/download/kindling_$(curl -s https://api.github.com/repos/kindling-sh/kindling/releases/latest | grep tag_name | cut -d '"' -f4 | sed 's/^v//')_darwin_amd64.tar.gz
tar xzf kindling.tar.gz
sudo mv kindling /usr/local/bin/
# Linux (amd64)
curl -Lo kindling.tar.gz https://github.com/kindling-sh/kindling/releases/latest/download/kindling_$(curl -s https://api.github.com/repos/kindling-sh/kindling/releases/latest | grep tag_name | cut -d '"' -f4 | sed 's/^v//')_linux_amd64.tar.gz
tar xzf kindling.tar.gz
sudo mv kindling /usr/local/bin/macOS Gatekeeper note: If you see "Apple could not verify kindling is free of malware", clear the quarantine flag:
sudo xattr -d com.apple.quarantine /usr/local/bin/kindling
git clone https://github.com/kindling-sh/kindling.git
cd kindling
make cli
sudo mv bin/kindling /usr/local/bin/| Tool | Version |
|---|---|
| Kind | 0.20+ |
| kubectl | 1.28+ |
| Docker | 24+ |
| Go | 1.25+ (only for building from source) |
| Workload | CPUs | Memory | Disk |
|---|---|---|---|
| Small (1–3 lightweight services) | 4 | 8 GB | 30 GB |
| Medium (4–6 services, mixed languages) | 6 | 12 GB | 50 GB |
| Large (7+ services, heavy compilers) | 8+ | 16 GB | 80 GB |
kindling generate scans your repository and uses AI (OpenAI or Anthropic) to produce a complete GitHub Actions workflow:
kindling generate -k <api-key> -r /path/to/my-appIt detects:
- docker-compose.yml — uses it as source of truth for build contexts, dependencies, and env vars
- Helm charts & Kustomize overlays — renders them for accurate port/env context
.envtemplate files — distinguishes external credentials from app config- External credentials — suggests
kindling secrets setfor each detected API key/token/DSN - OAuth/OIDC patterns — flags Auth0, Okta, Firebase Auth, etc. and suggests
kindling expose - Languages — Go, TypeScript, Python, Java, Rust, Ruby, PHP, C#, Elixir
Supports OpenAI reasoning models (o3, o3-mini) for complex multi-service projects:
kindling generate -k sk-... -r . --model o3 # maximum accuracy
kindling generate -k sk-... -r . --provider anthropic # use Anthropic
kindling generate -k sk-... -r . --dry-run # preview without writingExternal credentials are stored as Kubernetes Secrets with automatic local backup for cluster rebuilds:
kindling secrets set STRIPE_KEY sk_live_abc123 # store
kindling secrets list # list
kindling secrets restore # restore after cluster rebuildkindling expose creates a secure tunnel for OAuth callbacks:
kindling expose # auto-detect provider (cloudflared or ngrok)
kindling expose --stop # stop and restore ingressA Go web server with Postgres + Redis. → examples/sample-app/
Orders, Inventory, Gateway, and a React UI. Postgres, MongoDB, and Redis message queue. Great for testing multi-service sync workflows. → examples/microservices/
Go API + React dashboard with Postgres, Redis, Elasticsearch, Kafka, and Vault. → examples/platform-api/
| Command | Description |
|---|---|
kindling init |
Bootstrap Kind cluster + operator + registry + ingress |
kindling runners |
Register GitHub Actions self-hosted runner |
kindling generate |
AI-generate a GitHub Actions workflow |
kindling deploy |
Apply a DevStagingEnvironment from YAML |
kindling sync |
Live-sync files + hot reload (inner loop) |
kindling dashboard |
Launch the web dashboard |
kindling status |
Show cluster/environment status |
kindling logs |
Tail operator logs |
kindling secrets |
Manage external credentials |
kindling expose |
Public HTTPS tunnel for OAuth |
kindling env |
Set/list/unset env vars on deployments |
kindling reset |
Remove runner pool (keep cluster) |
kindling destroy |
Tear down the cluster |
.
├── cli/ # kindling CLI (Go + embedded dashboard)
│ ├── cmd/
│ │ ├── root.go # Cobra root command
│ │ ├── sync.go # Live sync + hot reload (30+ runtimes)
│ │ ├── dashboard.go # Web dashboard server
│ │ ├── dashboard_api.go # Dashboard read-only API handlers
│ │ ├── dashboard_actions.go # Dashboard mutation API (sync, load, deploy)
│ │ ├── dashboard-ui/ # React/TypeScript dashboard SPA
│ │ ├── generate.go # AI workflow generation
│ │ ├── secrets.go # Secret management
│ │ ├── expose.go # HTTPS tunnels
│ │ └── ...
│ └── main.go
├── api/v1alpha1/ # CRD type definitions
├── internal/controller/ # Operator reconcile logic
├── cmd/main.go # Operator entrypoint
├── .github/actions/ # Reusable GitHub Actions
│ ├── kindling-build/action.yml
│ └── kindling-deploy/action.yml
├── config/ # Kustomize manifests (CRDs, RBAC, etc.)
├── examples/ # Sample apps (single, microservices, platform)
├── docs/ # Documentation
├── kind-config.yaml # Kind cluster config
├── setup-ingress.sh # Ingress + registry installer
├── Makefile # Build targets
└── Dockerfile # Operator container image
make generate manifests # Regenerate deepcopy + CRD/RBAC
make test # Run tests
make cli # Build CLI → bin/kindling
make docker-build IMG=controller:dev- Local CI — self-hosted GitHub Actions runners in Kind
- 15 auto-provisioned dependency types (Postgres, Redis, Kafka, etc.)
- Kaniko container builds (no Docker daemon)
- Reusable GitHub Actions (
kindling-build+kindling-deploy) - AI workflow generation with docker-compose, Helm, and Kustomize awareness
-
kindling sync— live file sync with 30+ language-aware restart strategies - Automatic deployment rollback on sync stop
-
kindling dashboard— web UI with sync/load buttons and runtime detection - Frontend build pattern (React/Vue/Angular → nginx)
- Cross-compilation for compiled languages (Go, Rust, Java, .NET)
- Secrets management with local backup across cluster rebuilds
- Public HTTPS tunnels for OAuth (cloudflared/ngrok)
- Live env var management without redeploying
- Crash log diagnostics in
kindling status -
kindling export— generate production-ready Helm chart from cluster state -
kindling diagnose— LLM-powered error remediation - Stable callback URL relay for OAuth across tunnel reconnections
- Automatic TTL-based cleanup of stale environments
- Smart change detection — only rebuild services with changed files
Contributions welcome! Please open an issue to discuss your idea before submitting a PR.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Run
make test - Open a Pull Request
Apache License 2.0 — see LICENSE for full text.