KBkilterKB
userdev

kilter.yaml sits at your project root and configures both the dev environment and production deploys. It is deliberately minimal: every field is optional, and auto-detection fills in sensible defaults. Most projects start with an empty (or absent) file and only add what they need.

Example

# All fields are optional. Auto-detection fills in defaults.
name: my-app              # Project name (default: directory name)
port: 3000                # App port (default: parsed from dev command, e.g. vite --port 3001)
health: /api/health       # Health check path
image: node:22-alpine     # Base Docker image
dev: npm run dev          # Dev command
skills: true              # Generate agent skills (default: true, set false to disable)
 
# Services — simple list or inline overrides
services:
  - postgres              # Catalog reference
  - redis
  - name: imgproxy        # Inline custom service
    image: darthsim/imgproxy:latest
    port: 8080
    provides:
      IMGPROXY_URL: "http://imgproxy.{{ .Namespace }}.svc:8080"
 
# Environment variables injected into the app container
env:
  CUSTOM_VAR: "value"

Top-level fields

FieldDefaultPurpose
namedirectory nameProject name — also names the cluster, namespace, and cache dir
portparsed from the dev commandPort the app listens on
healthHealth check path for readiness probes
imagedetected from the frameworkBase Docker image for the dev container
devdetected from package.json etc.Command that starts the app in dev
skillstrueGenerate agent skills into the project; set false to disable

Services

The services: list declares backing services. Two shapes work:

Catalog reference — a bare string names a service from kilter catalog:

services:
  - postgres
  - redis

Inline override — an object defines or customizes a service in place:

services:
  - name: imgproxy
    image: darthsim/imgproxy:latest
    port: 8080
    provides:
      IMGPROXY_URL: "http://imgproxy.{{ .Namespace }}.svc:8080"

provides: declares env vars the service injects into the app container; {{ .Namespace }} is templated to the project namespace at render time.

Per-service top-level blocks are deprecated

Older configs override services with a top-level block like postgres: { database: my_db }. That syntax still works but is deprecated — use the inline object form inside services: instead.

Add services with kilter add <service> rather than editing by hand — it updates the file and re-renders artifacts in one step.

Env block

env: injects plain environment variables into the app container:

env:
  CUSTOM_VAR: "value"

Use this for non-secret configuration. Service connection strings come from each service's provides: — you don't declare DATABASE_URL here.

Manifest block

The optional manifest: block feeds the kilter portal's discovery registry (ADR-0016). It changes nothing about how the app runs — it describes the app to the platform.

manifest:
  description: "Customer-facing billing dashboard"
  owner: platform-team
  tags: [billing, internal]
  links:
    docs: https://kb.example.com/billing
    runbook: https://kb.example.com/billing/runbook
FieldPurpose
descriptionOne-line summary shown on the app's portal tile
ownerTeam or person responsible
tagsFree-form labels for filtering in the portal
linksMap of named URLs; docs and runbook are the conventional keys. A repo link is auto-filled from the git remote at deploy time
agentDeclares a2a/mcp paths; apps with this set are classified as Agents in the portal

Key concepts

Isolated kubeconfig

Kilter never touches ~/.kube/config. Each project's kubeconfig lives at:

~/.cache/kilter/<name>/kubeconfig

Access the cluster directly with KUBECONFIG=~/.cache/kilter/<name>/kubeconfig kubectl ....

Port isolation

Each project gets deterministic, non-conflicting port allocations, so multiple kilter projects run side by side without collisions. kilter env prints your project's ports and connection strings.

Eject pattern

Generated artifacts (Tiltfile, Dockerfiles, chart values) live in ~/.cache/kilter/<name>/. Run kilter eject to copy them into your project for customization — once ejected, kilter won't overwrite them. kilter eject --list shows what's available.

Auto-detection

Kilter scans package.json, go.mod, Cargo.toml, and pyproject.toml to detect:

  • Framework (Next.js, Go, Rust, Python)
  • Services (postgres from pg/drizzle-orm, redis from ioredis, and so on)
  • Port, dev command, and base image

Anything you set explicitly in kilter.yaml wins over detection.

Production-only fields

Fields like public: false, secrets:, appSecrets:, processes:, and environments: only apply to kilter deploy. They're covered in Deploying to Production.