
Tilt
- 249 installs
- 52 repo stars
- Updated June 24, 2026
- 0xbigboss/claude-code
Configure Tilt for fast local Kubernetes dev loops, live reload, and service wiring while integrating microservices during backend and platform work.
About
Teaches Claude to use Tilt for Kubernetes-centric local development: author Tiltfiles, wire microservices, enable live reload, manage resource dependencies, and accelerate integration testing for SaaS, API, and CLI-backed backends.
- Local Kubernetes dev environment setup
- Live reload and fast inner dev loops
- Multi-service wiring for microservices
- Tiltfile configuration and resource deps
- Saas and API service integration testing
Tilt by the numbers
- 249 all-time installs (skills.sh)
- Ranked #363 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/0xbigboss/claude-code --skill tiltAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 249 |
|---|---|
| repo stars | ★ 52 |
| Last updated | June 24, 2026 |
| Repository | 0xbigboss/claude-code ↗ |
What it does
Configure Tilt for fast local Kubernetes dev loops, live reload, and service wiring while integrating microservices during backend and platform work.
Files
Tilt
First Action: Check for Errors
Before investigating issues or verifying deployments, check resource health. Run errors first, separately from pending/in-progress — otherwise real failures get buried in 20+ pending lines:
# 1. Errors only — surface the buildHistory[0].error so you see WHY, not just THAT
tilt get uiresources -o json | jq -r '.items[] | select(.status.runtimeStatus == "error" or .status.updateStatus == "error") | "\(.metadata.name): runtime=\(.status.runtimeStatus) update=\(.status.updateStatus)\n reason: \((.status.buildHistory[0].error // "(no buildHistory error; check tilt logs)") | gsub("\n"; " ") | .[0:240])"'
# 2. In-progress and pending — informational; an in-progress build may flip to error any moment
tilt get uiresources -o json | jq -r '.items[] | select(.status.updateStatus == "in_progress" or .status.updateStatus == "pending" or .status.runtimeStatus == "pending") | "\(.metadata.name): runtime=\(.status.runtimeStatus) update=\(.status.updateStatus)"'
# 3. Docker-compose container health — MISSED by the error filter above.
# An `Up (unhealthy)` compose container keeps runtimeStatus=ok/update=ok, so
# queries 1-2 never flag it; the red UI badge comes from healthStatus here.
tilt get uiresources -o json | jq -r '.items[] | select(.status.composeResourceInfo.healthStatus == "unhealthy") | "\(.metadata.name): compose healthStatus=unhealthy (HEALTHCHECK failing — service may still be up)"'
# 4. Quick status overview
tilt get uiresources -o json | jq '[.items[].status.updateStatus] | group_by(.) | map({status: .[0], count: length})'If a resource is in_progress when you check, re-poll before declaring it healthy — it can transition straight to error with a populated buildHistory[0].error. The updateStatus field reflects only the current build attempt; the last error always lives in buildHistory[0].error even when updateStatus is none or not_applicable.
Docker-compose resources are a blind spot. Their runtimeStatus/updateStatus reflect only build/up state, NOT the container's docker HEALTHCHECK — so a probe-failing container (docker ps → Up (unhealthy)) reads runtimeStatus=ok and slips past queries 1-2, while the Tilt UI still reddens it. The authoritative signal is .status.composeResourceInfo.healthStatus (healthy / unhealthy / absent when the service has no healthcheck), which query 3 catches. These resources also have k8sResourceInfo: null (spec.type == "docker-compose"); to find why a probe fails, drop to docker: docker inspect <compose-project>-<svc> --format '{{json .State.Health}}' reads the probe's last exit code + output. A common cause is the healthcheck script invoking a CLI the image doesn't ship (e.g. curl/grpcurl removed in slimmed images) — the service is fine, the probe is broken.
Non-Default Ports
When Tilt runs on a non-default port, add --port:
tilt get uiresources --port 37035
tilt logs <resource> --port 37035Resource Status
# All resources with status
tilt get uiresources -o json | jq '.items[] | {name: .metadata.name, runtime: .status.runtimeStatus, update: .status.updateStatus}'
# Single resource detail
tilt get uiresource/<name> -o json
# Wait for ready
tilt wait --for=condition=Ready uiresource/<name> --timeout=120sStatus values:
- RuntimeStatus:
ok,error,pending,none,not_applicable - UpdateStatus:
ok,error,pending,in_progress,none,not_applicable
Logs
tilt logs <resource>
tilt logs <resource> --since 5m
tilt logs <resource> --tail 100
tilt logs --json # JSON Lines outputTrigger and Lifecycle
tilt trigger <resource> # Force update
tilt up # Start
tilt down # Stop and clean upRunning tilt up
Follow zmx skill patterns — check for existing sessions, derive name from git root, use zmx run (not attach):
PROJECT=$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" || basename "$PWD")
SESSION="${PROJECT}-tilt"
if zmx list --short 2>/dev/null | grep -q "^${SESSION}$"; then
echo "Tilt session already exists: $SESSION"
else
zmx run "$SESSION" 'tilt up'
echo "Started tilt in zmx session: $SESSION"
fiCritical: Never Restart for Code Changes
Tilt live-reloads automatically. Never suggest restarting `tilt up` for:
- Tiltfile edits
- Source code changes
- Kubernetes manifest updates
Restart only for: Tilt version upgrades, port/host changes, crashes, cluster context switches.
References
- TILTFILE_API.md - Tiltfile authoring
- CLI_REFERENCE.md - Complete CLI with JSON patterns
- https://docs.tilt.dev/
Tilt CLI Reference
Table of Contents
Resource Queries
List All Resources
tilt get uiresources -o jsonJSON structure:
{
"apiVersion": "tilt.dev/v1alpha1",
"kind": "UIResource",
"items": [{
"metadata": {"name": "resource-name"},
"status": {
"runtimeStatus": "unknown|none|pending|ok|error|not_applicable",
"updateStatus": "none|pending|in_progress|ok|error|not_applicable",
"triggerMode": "TriggerModeAuto|TriggerModeManual",
"queued": false,
"lastDeployTime": "2024-01-01T00:00:00Z",
"conditions": [...]
}
}]
}Get Single Resource
tilt get uiresource/<name> -o jsonDescribe Resource (Human-Readable)
tilt describe uiresource/<name>Note: describe outputs human-readable format only; use get -o json for structured output.
List Available Resource Types
tilt api-resourcesLogs
The tilt logs command supports --since, --tail, and --json flags for filtering and structured output.
Basic Usage
tilt logs # All logs
tilt logs <resource> # Filter by resource name
tilt logs -f # Follow/stream new logsTime-Based Filtering
tilt logs --since 5m # Logs from last 5 minutes
tilt logs --since 1h # Logs from last hour
tilt logs --since 30s # Logs from last 30 seconds
tilt logs <resource> --since 5m # Resource logs from last 5 minutesTail
tilt logs --tail 100 # Last 100 log lines
tilt logs --tail 50 -f # Last 50 lines, then followNote: --tail applies only to initial history when combined with -f.
JSON Output
tilt logs --json # Output as JSON Lines (JSONL)
tilt logs --json | jq . # Pipe to jq for processing
tilt logs --json --json-fields=full # Include all fields (even empty)
tilt logs --json --json-fields=time,resource,message # Custom fieldsAvailable fields: time, resource, level, message, spanID, progressID, buildEvent, source
Presets:
minimal(default): time, resource, level, messagefull: all fields including empty values
Search Patterns
tilt logs --since 5m | rg -i "error|fail" # Search for errors
tilt logs <resource> --tail 50 | rg "listening on" # Find startup
tilt logs --since 1m | rg -i "reload|restart|updated" # Verify updatesSource and Level Filtering
tilt logs --source build # Build logs only
tilt logs --source runtime # Runtime logs only
tilt logs --level warn # Warnings and above
tilt logs --level error # Errors onlyControl Commands
Trigger Manual Update
tilt trigger <resource>Forces an update even if no files changed.
Enable Resources
tilt enable <resource>
tilt enable <resource1> <resource2>
tilt enable --all # Enable all resources
tilt enable --labels=backend # Enable by labelDisable Resources
tilt disable <resource>
tilt disable <resource1> <resource2>
tilt disable --all # Disable all resources
tilt disable --labels=frontend # Disable by labelChange Tiltfile Args
tilt args -- --env=stagingUpdates args for running Tilt instance.
Wait Conditions
Wait for Ready
tilt wait --for=condition=Ready uiresource/<name>With Timeout
tilt wait --for=condition=Ready uiresource/<name> --timeout=120sWait for Multiple Resources
tilt wait --for=condition=Ready uiresource/api uiresource/webWait for All Resources
tilt wait --for=condition=Ready uiresource --allJSON Parsing Patterns
Extract All Resource Names
tilt get uiresources -o json | jq -r '.items[].metadata.name'Extract Failed Resources
tilt get uiresources -o json | jq -r '.items[] | select(.status.runtimeStatus == "error") | .metadata.name'Extract Pending Resources
tilt get uiresources -o json | jq -r '.items[] | select(.status.updateStatus == "pending" or .status.updateStatus == "in_progress") | .metadata.name'Check Specific Resource Status
tilt get uiresource/<name> -o json | jq '.status.runtimeStatus'Get Status Summary
tilt get uiresources -o json | jq '.items[] | {name: .metadata.name, runtime: .status.runtimeStatus, update: .status.updateStatus}'Get Last Deploy Times
tilt get uiresources -o json | jq '.items[] | {name: .metadata.name, deployed: .status.lastDeployTime}'Count Resources by Status
tilt get uiresources -o json | jq -r '.items | group_by(.status.runtimeStatus) | map({status: .[0].status.runtimeStatus, count: length})'Check if All Resources Ready
tilt get uiresources -o json | jq -e '[.items[].status.runtimeStatus] | all(. == "ok" or . == "not_applicable")'Returns exit code 0 if all ready, 1 otherwise.
Lifecycle Commands
Start Tilt
tilt up
tilt up --stream # Stream logs to terminal
tilt up --port=10351 # Custom API port
tilt up -- --env=dev # Pass args to TiltfileStop Tilt
tilt downRemoves resources created by tilt up.
CI Mode
tilt ci # Default timeout: 30m
tilt ci --timeout=10m # Custom timeoutRuns until all resources reach steady state or error, then exits.
Verify Installation
tilt verify-installVersion
tilt versionGlobal Flags
-d, --debug Enable debug logging
-v, --verbose Enable verbose logging
--klog int Kubernetes API logging (0-4: debug, 5-9: tracing)
--host string Host for Tilt API server (default "localhost")
--port int Port for Tilt API server (default 10350)Tiltfile API Reference
Table of Contents
Resource Types
local_resource
Runs commands on host machine.
local_resource(
'name',
cmd='command', # One-time command
serve_cmd='server', # Long-running process (optional)
deps=['file.txt'], # File dependencies trigger re-run
resource_deps=['other'], # Wait for other resources first
auto_init=True, # Run on tilt up (default: True)
allow_parallel=False, # Concurrent execution (default: False)
readiness_probe=probe(), # Health check for serve_cmd
trigger_mode=TRIGGER_MODE_AUTO, # AUTO or MANUAL
labels=['group'], # UI grouping
)cmd vs serve_cmd:
cmd: Runs once, re-runs on file changes or triggerserve_cmd: Long-running process, restarted on file changes
docker_build
Builds container images.
docker_build(
'image-name',
'.', # Build context
dockerfile='Dockerfile', # Dockerfile path (default: Dockerfile)
target='stage', # Multi-stage target (optional)
build_args={'ENV': 'dev'}, # Build arguments
only=['src/', 'go.mod'], # Include only these paths
ignore=['tests/', '*.md'], # Exclude paths
live_update=[...], # Fast sync without rebuild
)custom_build
Custom build commands for non-Docker builds.
custom_build(
'image-name',
'bazel build //app:image', # Build command
deps=['src/', 'BUILD'], # File dependencies
tag='dev', # Image tag
skips_local_docker=True, # Image not in local docker
live_update=[...],
)k8s_yaml
Loads Kubernetes manifests.
k8s_yaml('manifests.yaml')
k8s_yaml(['deploy.yaml', 'service.yaml'])
k8s_yaml(helm('chart/', values='values.yaml'))
k8s_yaml(kustomize('overlays/dev'))
k8s_yaml(local('kubectl kustomize .')) # Command outputk8s_resource
Configures Kubernetes resources.
k8s_resource(
'deployment-name',
port_forwards='8080:80', # Single forward
port_forwards=['8080:80', '9090'], # Multiple forwards
resource_deps=['database'], # Dependencies
objects=['configmap:my-config'], # Group additional objects
labels=['backend'], # UI grouping
trigger_mode=TRIGGER_MODE_MANUAL,
)docker_compose
Docker Compose integration.
docker_compose('docker-compose.yml')
docker_compose(['docker-compose.yml', 'docker-compose.override.yml'])dc_resource
Configures Docker Compose services.
dc_resource(
'service-name',
resource_deps=['setup'],
trigger_mode=TRIGGER_MODE_AUTO,
labels=['services'],
)Dependency Ordering
Explicit Dependencies
# Resource waits for dependencies before starting
k8s_resource('api', resource_deps=['database', 'redis'])
local_resource('migrate', resource_deps=['database'])Implicit Dependencies
# Image references create automatic dependencies
docker_build('myapp', '.')
k8s_yaml('deploy.yaml') # If uses myapp image, dependency is automaticTrigger Modes
# Manual trigger - only updates when explicitly triggered
k8s_resource('expensive-build', trigger_mode=TRIGGER_MODE_MANUAL)
# Auto trigger (default) - updates on file changes
k8s_resource('api', trigger_mode=TRIGGER_MODE_AUTO)
# Set default for all resources
trigger_mode(TRIGGER_MODE_MANUAL)Live Update
Fast container updates without full rebuild.
Step ordering matters: 1. fall_back_on() steps must come FIRST 2. sync() steps come next 3. run() steps must come AFTER sync steps
docker_build(
'myapp',
'.',
live_update=[
# 1. Full rebuild triggers (must be first)
fall_back_on(['package.json', 'package-lock.json']),
# 2. Sync files to container
sync('./src', '/app/src'),
# 3. Run commands after sync
run('npm run build', trigger=['./src']),
]
)Live Update Steps
fall_back_on(['package.json']) # Force full rebuild (must be first)
sync('./local/path', '/container/path') # Copy files
run('command') # Run in container
run('command', trigger=['./src']) # Run only when trigger files change
run('command', echo_off=True) # Run without echoing command
restart_container() # Restart container processConfiguration
CLI Arguments
config.define_string('env', args=True, usage='Environment name')
config.define_bool('debug', usage='Enable debug mode')
config.define_string_list('services', usage='Services to enable')
cfg = config.parse()
env = cfg.get('env', 'dev')
if cfg.get('debug'):
local_resource('debug-tools', ...)Usage: tilt up -- --env=staging --debug
Selective Resources
# Only enable specific resources
config.set_enabled_resources(['api', 'web'])
# Clear and set new list
config.clear_enabled_resources()
config.set_enabled_resources(['database'])Context Validation
# Only allow specific k8s contexts
allow_k8s_contexts(['docker-desktop', 'minikube', 'kind-*'])
# Get current context
ctx = k8s_context()
ns = k8s_namespace()Default Registry
# Push images to registry instead of loading directly
default_registry('gcr.io/my-project')
default_registry('localhost:5000', single_name='dev')Extensions
Loading Extensions
load('ext://restart_process', 'docker_build_with_restart')
load('ext://namespace', 'namespace_create', 'namespace_inject')
load('ext://git_resource', 'git_checkout')Extensions are loaded from https://github.com/tilt-dev/tilt-extensions
Custom Extension Repository
v1alpha1.extension_repo(
name='my-extensions',
url='https://github.com/org/tilt-extensions',
ref='v1.0.0'
)
load('ext://my-extensions/my-ext', 'my_function')Data Handling
Reading Files
content = read_file('config.yaml')
data = read_json('config.json')
data = read_yaml('config.yaml')Encoding/Decoding
obj = decode_json('{"key": "value"}')
obj = decode_yaml('key: value')
yaml_list = decode_yaml_stream(multi_doc_yaml)
json_str = encode_json(obj)
yaml_str = encode_yaml(obj)Filtering YAML
# Filter by kind
deployments = filter_yaml(manifests, kind='Deployment')
# Filter by name
api = filter_yaml(manifests, name='api')
# Filter by labels
selected = filter_yaml(manifests, labels={'app': 'myapp'})File Operations
Watch Files
# Explicit file watching
watch_file('config/settings.yaml')
# List directory contents (automatically watched)
files = listdir('manifests/', recursive=True)Local Commands
# Run command and capture output
output = local('kubectl get nodes -o name')
# Run without capturing
local('echo "Hello"', quiet=True)
# With environment variables
local('my-script.sh', env={'DEBUG': '1'})Path Operations
cwd = os.getcwd()
exists = os.path.exists('file.txt')
joined = os.path.join('dir', 'file.txt')
base = os.path.basename('/path/to/file.txt')
dir = os.path.dirname('/path/to/file.txt')UI Customization
Labels (Grouping)
k8s_resource('api', labels=['backend'])
k8s_resource('web', labels=['frontend'])
local_resource('tests', labels=['ci'])Links
k8s_resource('api', links=[
link('http://localhost:8080', 'API'),
link('http://localhost:8080/docs', 'Swagger'),
])Update Settings
update_settings(
max_parallel_updates=3, # Concurrent updates
k8s_upsert_timeout_secs=60,
suppress_unused_image_warnings=['base-image'],
)CI Settings
ci_settings(
k8s_grace_period='10s', # Shutdown grace period
timeout='10m', # Overall timeout
)