
Tilt
- 36 installs
- 25 repo stars
- Updated July 31, 2026
- hyperb1iss/hyperskills
Writes and debugs Tiltfiles for local Kubernetes development: live update, docker/custom builds, k8s and local resources, logs, and CI.
About
Covers automating the local Kubernetes dev loop with Tilt (watch, build, deploy) via a Starlark Tiltfile. A developer uses it to write a Tiltfile, set up live_update, add resources, or debug a running Tilt instance.
- CLI lifecycle: tilt up/ci/down and resource management
- live_update, docker_build, custom_build, k8s_resource patterns
Tilt by the numbers
- 36 all-time installs (skills.sh)
- +3 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #839 of 1,435 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hyperb1iss/hyperskills --skill tiltAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 36 |
|---|---|
| repo stars | ★ 25 |
| Last updated | July 31, 2026 |
| Repository | hyperb1iss/hyperskills ↗ |
What it does
Writes and debugs Tiltfiles for local Kubernetes development: live update, docker/custom builds, k8s and local resources, logs, and CI.
Files
Tilt: Kubernetes Dev Toolkit
Tilt automates the local Kubernetes development loop: watch files, build images, deploy to cluster. Configuration lives in a Tiltfile (Starlark, a Python dialect). A resource bundles an image build + k8s deploy (or a local command) into a single manageable unit.
CLI Operations
The commands an agent uses to interact with a running Tilt instance.
Lifecycle
| Task | Command |
|---|---|
| Start dev environment | tilt up [-- <Tiltfile args>] |
| Start with terminal log streaming | tilt up --stream |
| Start specific resources only | tilt up frontend backend |
| Run in CI/batch mode (exits on success/failure) | tilt ci --timeout 30m |
| Stop and delete deployed resources | tilt down |
| Change runtime Tiltfile args | tilt args -- --flag value |
| Change runtime args (clear all) | tilt args --clear |
On Ctrl+C from tilt up: K8s and Docker Compose resources keep running. Local serve_cmd processes stop. Use tilt down to clean up.
Viewing Logs
| Task | Command |
|---|---|
| Stream all logs | tilt logs -f |
| Stream logs for one resource | tilt logs -f <resource> |
| Show only errors | tilt logs --level error |
| Show build logs only | tilt logs --source build |
| Show runtime logs only | tilt logs --source runtime |
| Logs since 5 minutes ago | tilt logs --since 5m |
| Last 100 lines | tilt logs --tail 100 |
| JSON output (for parsing) | tilt logs --json |
Resource Management
| Task | Command |
|---|---|
| List all resources | tilt get uiresources |
| Resource status as JSON | tilt get uiresources -o json |
| Describe a resource in detail | tilt describe uiresource <name> |
| Force rebuild a resource | tilt trigger <resource> |
| Enable a disabled resource | tilt enable <resource> |
| Disable a resource | tilt disable <resource> |
| Wait for resource readiness | tilt wait --for=condition=Ready uiresource/<name> |
Inspection & Debugging
| Task | Command |
|---|---|
| Diagnostics (versions, cluster) | tilt doctor |
| Inspect file watches | tilt get filewatches |
| Describe a specific file watch | tilt describe filewatch <name> |
| Full engine state dump (JSON) | tilt dump engine |
| Full UI state dump | tilt dump webview |
| Test Docker build as Tilt would | tilt docker -- build <args> |
| List API resource types | tilt api-resources |
The Tilt API server runs on localhost:10350 by default. All tilt get/describe/trigger commands talk to it.
Build Strategy Selector
| Situation | Function | Key detail |
|---|---|---|
| Standard Dockerfile | docker_build(ref, context) | Watches context dir, auto-injects into k8s |
| Custom toolchain (Bazel, ko, Buildpacks) | custom_build(ref, cmd, deps) | Must tag with $EXPECTED_REF env var |
| Non-Docker builder (Buildah, kaniko) | custom_build(..., skips_local_docker=True) | Builder handles push independently |
| Docker Compose services | docker_compose(configPaths) | Manages compose lifecycle |
| Helm charts | k8s_yaml(helm('./chart')) | Renders locally, deploys to cluster |
| Kustomize overlays | k8s_yaml(kustomize('./overlay')) | Renders locally, deploys to cluster |
Live Update Decision Tree
Live update replaces full image rebuilds with in-place container file syncs, seconds instead of minutes.
| Step | Purpose | Ordering |
|---|---|---|
fall_back_on(files) | Force full rebuild when these files change | Must come first |
sync(local, remote) | Copy changed files into running container | After fall_back_on |
run(cmd, trigger=files) | Execute command in container (e.g., install deps) | After sync |
restart_container() | Restart the container process | Must come last |
docker_build('myapp', '.', live_update=[
fall_back_on(['requirements.txt']),
sync('./src', '/app/src'),
run('pip install -r requirements.txt', trigger=['requirements.txt']),
])When live update breaks: Changes to files outside the docker_build context trigger a full rebuild. Changes outside any sync() path also trigger a full rebuild. First tilt up always does a full build, live update requires a running container.
Resource Configuration
# Kubernetes resource with port forwarding and dependencies
k8s_resource('frontend',
port_forwards=['3000:3000'],
resource_deps=['api', 'database'],
labels=['web'],
trigger_mode=TRIGGER_MODE_MANUAL,
)
# Local resource (build tool, test runner, code generator)
local_resource('codegen',
cmd='make generate',
deps=['./proto'],
labels=['tools'],
)
# Local server (runs continuously)
local_resource('storybook',
serve_cmd='npm run storybook',
deps=['./src/components'],
allow_parallel=True,
readiness_probe=probe(http_get=http_get_action(port=6006)),
)Parallelism: Local resources run serially by default. Set allow_parallel=True for independent resources. Image builds default to 3 concurrent, adjust with update_settings(max_parallel_updates=N).
Dependencies: resource_deps gates on first-ever readiness only, once a dependency is ready once, dependents unlock permanently for that session.
Debugging Flow
Service crashing? → tilt logs -f <resource> --source runtime
Build failing? → tilt logs -f <resource> --source build
tilt docker -- build <args> (reproduces Tilt's exact build)
Wrong files rebuild? → tilt get filewatches
tilt describe filewatch <name>
Check .tiltignore, watch_settings(ignore=), ignore= param
Force a rebuild? → tilt trigger <resource>
Resource stuck? → tilt describe uiresource <name>
Check resource_deps chain
For CRDs: pod_readiness='ignore'
General diagnostics? → tilt doctor
Full state dump? → tilt dump engine | jq .Top 10 Pitfalls
| Pitfall | Fix |
|---|---|
local() calls don't track file deps | Wrap with read_file() or add watch_file() |
| Live update paths outside docker_build context | Ensure sync local paths fall within context dir |
| Local resources block each other | Set allow_parallel=True on independent resources |
resource_deps doesn't re-gate on updates | It only checks first-ever readiness, not current version |
| Starlark has no while/try-except/class/recursion | Use for loops, fail() for errors, dicts for state |
.tiltignore doesn't affect Docker build context | Use .dockerignore to exclude from both rebuild triggers AND context |
$EXPECTED_REF not used in custom_build | Build script MUST tag the image with this env var |
run() trigger files not in a sync() step | Trigger paths must also be covered by a sync step |
First tilt up always does full build | Live update cannot work until a container is running |
| CRD pods stuck in pending | Set pod_readiness='ignore' on CRD resources |
Additional Resources
Reference Files
For detailed API signatures and advanced patterns, consult:
- `references/api-reference.md`: Complete Tiltfile API catalog organized by category, Starlark language notes, ignore mechanism comparison
- `references/patterns.md`: Multi-service architectures, environment config, CI integration, performance optimization, programmatic Tilt interaction, extension ecosystem
Anti-Patterns
| Anti-Pattern | Fix |
|---|---|
| Starting or stopping Tilt without consent | Ask before changing long-running dev environments |
Treating tilt up as a build command | Use tilt ci for batch verification |
| Live update without fallbacks | Put build/dependency files in fall_back_on |
| Debugging from Kubernetes YAML only | Inspect uiresources, file watches, and logs |
Using local() for watched shell work | Use local_resource with explicit deps |
What This Skill is NOT
- Not a Kubernetes primer.
- Not permission to run, restart, or tear down a dev environment.
- Not a substitute for reading
tilt doctorand resource logs.
Tiltfile API Reference
Complete catalog of Tiltfile built-in functions organized by category.
Build Functions
docker_build()
Build a Docker image and auto-inject into Kubernetes resources.
docker_build(
ref, # Image reference (e.g., 'myapp' or 'gcr.io/proj/myapp')
context, # Build context directory
build_args={}, # Docker build arguments dict
dockerfile='Dockerfile', # Path to Dockerfile (relative to context)
dockerfile_contents='', # Inline Dockerfile string (alternative to file)
live_update=[], # List of live update steps
match_in_env_vars=False, # Also match image ref in env vars
ignore=[], # Exclude patterns (dockerignore syntax)
only=[], # Include only these paths (not globs)
entrypoint=[], # Override container entrypoint
target='', # Multi-stage build target
ssh='', # SSH agent forwarding for builds
network='', # Docker build network mode
secret=[], # Build secrets
extra_tag=[], # Additional image tags
container_args=[], # Additional container arguments
cache_from=[], # External cache sources
pull=False, # Always pull base image
platform='', # Target platform (e.g., 'linux/amd64')
extra_hosts=[], # Extra /etc/hosts entries
)custom_build()
Build an image using any external tool. The build command MUST tag the image with $EXPECTED_REF.
custom_build(
ref, # Image reference
command, # Build command (shell string)
deps, # List of paths to watch
tag='', # Hardcoded tag override
disable_push=False, # Skip pushing to registry
skips_local_docker=False, # True for non-Docker builders (Buildah, kaniko)
live_update=[], # Live update steps
match_in_env_vars=False, # Match in env vars
ignore=[], # Exclude patterns
entrypoint=[], # Override entrypoint
command_bat='', # Windows-specific command
outputs_image_ref_to='', # File where script writes image ref
image_deps=[], # Other image refs this depends on
env={}, # Environment variables for build command
dir='', # Working directory for build command
)docker_compose()
Run services via Docker Compose.
docker_compose(
configPaths, # Path(s) to docker-compose.yml
env_file='', # Env file path
project_name='', # Compose project name
profiles=[], # Compose profiles to activate
wait=False, # Wait for services to be healthy
)default_registry()
Configure a default image registry for all builds.
default_registry(
host, # Registry host (e.g., 'gcr.io/my-project')
host_from_cluster='',# Registry host as seen from inside cluster
single_name='', # Use single image name with different tags
)Kubernetes Functions
k8s_yaml()
Load Kubernetes manifests into Tilt.
k8s_yaml(yaml, allow_duplicates=False)
# yaml can be: file path, list of paths, Blob from helm()/kustomize()/local()k8s_resource()
Configure how Tilt manages a Kubernetes resource.
k8s_resource(
workload, # Resource name (matches workload from k8s_yaml)
new_name='', # Rename the resource in Tilt UI
port_forwards=[], # List of port forwards (str or port_forward())
extra_pod_selectors=[], # Additional label selectors for pod matching
trigger_mode=None, # TRIGGER_MODE_AUTO or TRIGGER_MODE_MANUAL
resource_deps=[], # Resources that must be ready first
objects=[], # Non-workload objects to attach (e.g., ConfigMaps)
auto_init=True, # Start automatically on tilt up
pod_readiness='', # 'wait' (default), 'ignore', or 'connection'
links=[], # URLs shown in Tilt UI
labels=[], # UI grouping labels
discovery_strategy='', # How Tilt discovers pods for this resource
)k8s_custom_deploy()
Custom deploy command (not YAML-based).
k8s_custom_deploy(
name, # Resource name
apply_cmd, # Shell command to apply (must output YAML to stdout)
delete_cmd, # Shell command to delete
deps=[], # File dependencies
image_selector='', # Image ref to inject
live_update=[], # Live update steps
apply_dir='', # Working dir for apply
apply_env={}, # Env vars for apply
container_selector='', # Container name to target
image_deps=[], # Image dependencies
)helm()
Render a Helm chart to YAML. Returns a Blob for use with k8s_yaml().
helm(
pathToChartDir, # Path to Helm chart
name='', # Release name
namespace='', # Target namespace
values=[], # Values file paths
set=[], # Individual value overrides ('key=value')
kube_version='', # Kubernetes version for template rendering
skip_crds=False, # Skip CRD installation
)kustomize()
Render Kustomize overlays. Returns a Blob.
kustomize(pathToDir, kustomize_bin='', flags=[])filter_yaml()
Filter YAML by labels, name, namespace, kind, or api_version. Returns matching objects.
filter_yaml(yaml, labels=None, name='', namespace='', kind='', api_version='')k8s_kind()
Register a custom Kubernetes resource type.
k8s_kind(kind, api_version='', image_json_path=[], image_object=None, pod_readiness='')Context Functions
k8s_context() # Returns current k8s context name
k8s_namespace() # Returns current k8s namespace
allow_k8s_contexts(contexts) # Whitelist allowed contexts (safety check)Local Functions
local_resource()
Define a local command or server as a Tilt resource.
local_resource(
name, # Resource name
cmd='', # Build/task command (runs to completion)
deps=[], # File paths that trigger re-execution
trigger_mode=None, # TRIGGER_MODE_AUTO or TRIGGER_MODE_MANUAL
resource_deps=[], # Resources that must be ready first
ignore=[], # Exclude patterns
auto_init=True, # Start automatically
serve_cmd='', # Server command (runs continuously)
cmd_bat='', # Windows-specific command
serve_cmd_bat='', # Windows-specific serve command
allow_parallel=False, # Allow parallel execution with other local resources
links=[], # URLs shown in Tilt UI
labels=[], # UI grouping labels
env={}, # Env vars for cmd
serve_env={}, # Env vars for serve_cmd
readiness_probe=None, # Probe for serve_cmd readiness
dir='', # Working directory for cmd
serve_dir='', # Working directory for serve_cmd
)local()
Execute a shell command during Tiltfile evaluation. Returns a Blob.
local(command, quiet=False, command_bat='', echo_off=False, env={}, dir='', stdin='')Gotcha: local() does NOT automatically watch the files it reads. Pair with watch_file() or read_file() to establish file dependencies.
Live Update Steps
Steps are ordered: fall_back_on first, then sync, then run, then restart_container.
fall_back_on(files) # Force full rebuild when these files change
sync(local_path, remote_path) # Copy files to container
run(cmd, trigger=None) # Run command in container (trigger=files for conditional)
restart_container() # Restart the container processConstraint: run() trigger paths must also be covered by a preceding sync() step.
Configuration Functions
config.define_string('key') # Define string setting
config.define_string_list('key') # Define string list setting
config.define_bool('key') # Define boolean setting
config.define_string('key', args=True)# Positional argument
cfg = config.parse() # Parse and return config dict
config.set_enabled_resources(list) # Set initially enabled resources
config.clear_enabled_resources() # Disable all resources initiallySettings come from tilt_config.json (overridden by CLI args tilt up -- --key value). Update at runtime with tilt args -- --key value.
File I/O Functions
read_file(path, default=None) # Read file, return Blob (also establishes watch)
read_json(path, default=None) # Read and parse JSON file
read_yaml(path, default=None) # Read and parse YAML file
read_yaml_stream(path, default=None) # Read multi-document YAML
watch_file(path) # Watch file for changes (no read)
listdir(directory, recursive=False) # List directory contentsEncoding Functions
encode_json(obj) # Dict/list -> JSON string
decode_json(json_str) # JSON string -> dict/list
encode_yaml(obj) # Dict/list -> YAML string
decode_yaml(yaml_str) # YAML string -> dict/list
encode_yaml_stream(objs) # List of dicts -> multi-doc YAML
decode_yaml_stream(yaml_str) # Multi-doc YAML -> list of dicts
blob(string) # Wrap string as Blob typeSettings Functions
update_settings(
max_parallel_updates=3, # Concurrent image builds (default 3)
k8s_upsert_timeout_secs=30, # Timeout for k8s apply
suppress_unused_image_warnings=[], # Image refs to suppress warnings for
)
ci_settings(
k8s_grace_period='', # Grace period for k8s resource deletion
timeout='30m', # CI timeout (default 30 minutes)
readiness_timeout='5m', # Readiness check timeout
)
version_settings(check_updates=True, constraint='') # Version checking
trigger_mode(TRIGGER_MODE_AUTO) # Default trigger mode for all resources
watch_settings(ignore=[]) # Global file watch ignore patterns
secret_settings(disable_scrub=False) # Disable secret scrubbing in logs
docker_prune_settings(disable=False, max_age_mins=360, num_builds=0, interval_hrs=1, keep_recent=2)Control Flow
load(path, *symbols) # Import symbols from another Tiltfile
load_dynamic(path) # Import and return all globals as dict
fail(msg) # Stop execution with error
warn(msg) # Emit warning (continues execution)
exit(code=0) # Stop execution without error
enable_feature(name) # Enable experimental featureOS Module
os.getcwd() # Current working directory
os.getenv(key, default='') # Get environment variable
os.putenv(key, value) # Set environment variable
os.path.abspath(path) # Absolute path
os.path.basename(path) # Base name
os.path.dirname(path) # Directory name
os.path.exists(path) # Path exists check
os.path.join(path, *paths) # Join paths
os.path.realpath(path) # Canonical path
os.path.relpath(targpath, basepath) # Relative path
os.name # OS name ('posix' or 'nt')
os.environ # Environment variables dictGlobal Variables
config.main_dir # Directory containing the main Tiltfile
config.main_path # Full path to the main Tiltfile
config.tilt_subcommand # 'up', 'ci', or 'down'
sys.argv # Tiltfile arguments
sys.executable # Path to Tilt binaryIgnore Mechanism Comparison
| Mechanism | Scope | Prevents rebuild? | Affects Docker context? |
|---|---|---|---|
.dockerignore | docker_build only | Yes | Yes |
.tiltignore | All resources | Yes | No |
ignore= param | Per-build/resource | Yes | For docker_build: Yes |
only= param | docker_build only | Yes (inverse) | Yes |
watch_settings(ignore=) | Global | Yes | No |
Starlark Language Notes
Available: for loops, if/elif/else, list comprehensions, string formatting (% and .format()), *args/**kwargs, nested functions, lambda (limited).
NOT available: while loops, try/except, class definitions, recursion, import (use load()), set literals (use set() function), generators/yield, async/await, with statements.
Frozen values: Variables from loaded files are frozen (immutable). Modify by creating new values, not mutating in place.
Built-in functions: abs, all, any, bool, dict, dir, enumerate, fail, float, getattr, hasattr, hash, int, len, list, max, min, print, range, repr, reversed, sorted, str, tuple, type, zip.
String methods: capitalize, count, endswith, find, format, index, isalnum, isalpha, isdigit, islower, isspace, istitle, isupper, join, lower, lstrip, partition, removeprefix, removesuffix, replace, rfind, rindex, rpartition, rsplit, rstrip, split, splitlines, startswith, strip, title, upper.
Dict methods: clear, get, items, keys, pop, popitem, setdefault, update, values.
List methods: append, clear, extend, index, insert, pop, remove.
Tilt Power Patterns
Advanced configuration and operational patterns for real-world Tiltfiles.
Multi-Service Architecture
Tiltfile Organization
For projects with 10+ services, split configuration across files and compose with load():
# Root Tiltfile
load('./services/frontend/Tiltfile', 'frontend_resources')
load('./services/backend/Tiltfile', 'backend_resources')
load('./services/infra/Tiltfile', 'infra_resources')
# Group in UI with labels
k8s_resource('frontend', labels=['web'])
k8s_resource('api', labels=['backend'])
k8s_resource('postgres', labels=['infra'])
k8s_resource('redis', labels=['infra'])Shared Tiltfile Libraries
# lib/helpers.Tiltfile
def standard_service(name, path, port, deps=[]):
docker_build('myco/' + name, path, live_update=[
sync(path + '/src', '/app/src'),
])
k8s_yaml(path + '/k8s.yaml')
k8s_resource(name,
port_forwards=[str(port) + ':' + str(port)],
resource_deps=deps,
labels=['services'],
)
# Root Tiltfile
load('./lib/helpers.Tiltfile', 'standard_service')
standard_service('users', './services/users', 8001)
standard_service('orders', './services/orders', 8002, deps=['users'])Environment-Based Configuration
User-Configurable Tiltfiles
# Define settings
config.define_string_list('to-run', args=True)
config.define_string_list('to-edit')
config.define_bool('with-monitoring')
cfg = config.parse()
# tilt_config.json (checked into repo as defaults)
# {"to-run": ["frontend", "api"], "to-edit": ["frontend"]}
# Select resources
resources = cfg.get('to-run', ['frontend', 'api', 'worker'])
config.set_enabled_resources(resources)
# Conditional live update only for services being edited
editable = cfg.get('to-edit', [])
for svc in all_services:
lu = [sync('./' + svc + '/src', '/app/src')] if svc in editable else []
docker_build('myco/' + svc, './' + svc, live_update=lu)
# Conditional monitoring stack
if cfg.get('with-monitoring', False):
k8s_yaml('./monitoring/prometheus.yaml')
k8s_yaml('./monitoring/grafana.yaml')Runtime changes: tilt args frontend api -- --to-edit frontend reconfigures without restart.
Preset Service Groups
config.define_string('profile')
cfg = config.parse()
profiles = {
'minimal': ['api', 'postgres'],
'frontend': ['api', 'frontend', 'postgres'],
'full': ['api', 'frontend', 'worker', 'postgres', 'redis', 'monitoring'],
}
profile = cfg.get('profile', 'minimal')
config.set_enabled_resources(profiles.get(profile, profiles['minimal']))Advanced Live Update Patterns
Hot Reload (No Restart Needed)
For frameworks with built-in hot reload (React, Next.js, Flask debug mode):
docker_build('myco/frontend', './frontend', live_update=[
sync('./frontend/src', '/app/src'),
sync('./frontend/public', '/app/public'),
# No restart_container() — framework watches files internally
])Conditional Dependency Install
docker_build('myco/api', './api', live_update=[
fall_back_on(['Dockerfile']),
sync('./api', '/app'),
run('pip install -r requirements.txt', trigger=['./api/requirements.txt']),
run('npm install', trigger=['./api/package.json']),
])Process Restart via entr
For containers without shell-based restart support (distroless, scratch):
# In Dockerfile: CMD echo /tmp/restart | entr -rz /app/server
docker_build('myco/api', './api', live_update=[
sync('./api/src', '/app/src'),
run('date > /tmp/restart'), # Touch trigger file, entr restarts process
])Monorepo with Selective Context
docker_build('myco/api', '.', dockerfile='services/api/Dockerfile',
only=['services/api', 'packages/shared', 'packages/types'],
live_update=[
sync('./services/api/src', '/app/src'),
sync('./packages/shared/src', '/app/node_modules/@myco/shared/src'),
],
)Performance Optimization
Build Caching
# Layer ordering: deps first, code last
# Dockerfile:
# COPY package.json .
# RUN npm install
# COPY . .
# Tilt: use only= to limit context
docker_build('myco/app', '.', only=['src', 'package.json', 'tsconfig.json'])Parallel Updates
# Increase concurrent builds (default 3)
update_settings(max_parallel_updates=10)
# Allow independent local resources to run in parallel
local_resource('lint', cmd='npm run lint', deps=['./src'], allow_parallel=True)
local_resource('typecheck', cmd='tsc --noEmit', deps=['./src'], allow_parallel=True)Ignore Patterns
# Global: skip test files, docs, CI config from triggering rebuilds
watch_settings(ignore=[
'**/*_test.go',
'**/testdata/**',
'docs/**',
'.github/**',
])
# Per-build: skip non-essential files from Docker context
docker_build('myco/api', '.', ignore=[
'**/*_test.go',
'**/testdata',
'README.md',
'.git',
]).tiltignore
Place in the same directory as the Tiltfile. Uses .dockerignore syntax. Prevents rebuilds but does NOT affect Docker build context.
# .tiltignore
*.md
docs/
.github/
**/*_test.goCI Integration
tilt ci Mode
tilt ci runs Tilt in batch mode: builds all resources, waits for readiness, exits 0 on success.
# Tiltfile CI settings
ci_settings(
timeout='30m', # Overall timeout (default 30m, 0 = no timeout)
readiness_timeout='5m', # Per-resource readiness timeout
k8s_grace_period='10s', # Grace period for pod termination
)GitHub Actions
- name: Setup cluster
uses: helm/kind-action@v1
- name: Install Tilt
uses: yokawasa/action-setup-tools@v0.9.0
with:
tilt: "0.36.3"
- name: Run Tilt CI
run: tilt ci -- --profile ciConditional CI Behavior
if config.tilt_subcommand == 'ci':
# Skip dev-only resources in CI
config.set_enabled_resources(['api', 'worker', 'integration-tests'])
update_settings(max_parallel_updates=1) # Conserve CI resources
else:
# Dev mode: everything enabled
passProgrammatic Tilt Interaction
Scripting with tilt get
# Check if all resources are ready
tilt get uiresources -o json | jq '.items[] | {name: .metadata.name, status: .status.runtimeStatus}'
# Wait for a specific resource
tilt wait --for=condition=Ready --timeout=120s uiresource/api
# Get resource names
tilt get uiresources -o name
# Watch for status changes
tilt get uiresources -w -o jsonLog Monitoring
# Stream JSON logs for parsing
tilt logs --json -f | jq 'select(.level == "error")'
# Filter by resource and source
tilt logs -f api --source runtime --since 5mDynamic Resource Management
# Disable expensive resources when not needed
tilt disable monitoring grafana prometheus
# Re-enable when debugging
tilt enable monitoring
# Trigger rebuild after external change
tilt trigger apiPort Forwarding Patterns
# Simple
k8s_resource('api', port_forwards='8080')
# Explicit mapping
k8s_resource('api', port_forwards=['8080:8080', '9090:9090'])
# Named with UI links
k8s_resource('api', port_forwards=[
port_forward(8080, 8080, name='API'),
port_forward(9090, 9090, name='Metrics'),
])
# Custom links (no port forward, just UI link)
k8s_resource('api', links=[
link('http://localhost:8080/docs', 'API Docs'),
link('http://localhost:8080/health', 'Health'),
])Extension Ecosystem
Load extensions from the community repository:
# v1alpha1 API (recommended)
v1alpha1.extension_repo(name='default', url='https://github.com/tilt-dev/tilt-extensions')
v1alpha1.extension(name='restart_process', repo_name='default', repo_path='restart_process')
# Shorthand (auto-discovers from default repo)
load('ext://restart_process', 'docker_build_with_restart')Key Extensions
| Extension | Purpose | Import |
|---|---|---|
restart_process | Restart container process on live update | load('ext://restart_process', 'docker_build_with_restart') |
helm_remote | Deploy Helm charts from remote repos | load('ext://helm_remote', 'helm_remote') |
namespace | Create namespace if it doesn't exist | load('ext://namespace', 'namespace_create') |
secret | Create k8s secrets from local values | load('ext://secret', 'secret_create_generic') |
configmap | Create ConfigMaps from files/literals | load('ext://configmap', 'configmap_create') |
git_resource | Deploy from a git repo | load('ext://git_resource', 'git_checkout') |
uibutton | Add custom buttons to Tilt UI | load('ext://uibutton', 'cmd_button') |
ko | Build Go images with ko | load('ext://ko', 'ko_build') |
pack | Build with Cloud Native Buildpacks | load('ext://pack', 'pack') |
dotenv | Load .env files | load('ext://dotenv', 'dotenv') |
cancel | Add cancel buttons to resources | load('ext://cancel', 'register') |
local_output | Capture local command output | load('ext://local_output', 'local_output') |
Custom UI Buttons
load('ext://uibutton', 'cmd_button', 'location')
# Add a button to a resource
cmd_button('seed-db',
argv=['make', 'seed'],
resource='database',
icon_name='database',
text='Seed Database',
)
# Add a global nav button
cmd_button('run-all-tests',
argv=['make', 'test'],
location=location.NAV,
icon_name='check_circle',
text='Run Tests',
)Custom Build Patterns
ko (Go images)
load('ext://ko', 'ko_build')
ko_build('myco/api', './cmd/api', deps=['./cmd/api', './pkg'])Buildpacks
load('ext://pack', 'pack')
pack('myco/api', path='./api', builder='paketobuildpacks/builder:base')Bazel
custom_build(
'myco/api',
'bazel run //api:image -- --norun && docker tag bazel/api:image $EXPECTED_REF',
deps=['./api', './proto'],
)Skipping local Docker (remote builders)
custom_build(
'myco/api',
'buildah bud -t $EXPECTED_REF ./api && buildah push $EXPECTED_REF',
deps=['./api'],
skips_local_docker=True,
)Readiness Probes
Local Resource Readiness
local_resource('dev-server',
serve_cmd='npm start',
readiness_probe=probe(
http_get=http_get_action(port=3000, path='/health'),
initial_delay_secs=5,
period_secs=2,
),
)Custom TCP Probe
local_resource('grpc-server',
serve_cmd='./server',
readiness_probe=probe(
tcp_socket=tcp_socket_action(port=50051),
period_secs=3,
),
)Exec Probe
local_resource('worker',
serve_cmd='celery -A app worker',
readiness_probe=probe(
exec=exec_action(['celery', '-A', 'app', 'inspect', 'ping']),
period_secs=10,
failure_threshold=5,
),
)Migration from Docker Compose
# Simplest migration: pass docker-compose.yml to Tilt
docker_compose('./docker-compose.yml')
# Configure individual services
dc_resource('api',
trigger_mode=TRIGGER_MODE_AUTO,
resource_deps=['postgres'],
labels=['backend'],
)
# Add live update to a compose service
docker_build('myco/api', './api',
live_update=[sync('./api/src', '/app/src')],
)Workload-to-Resource Naming
When Tilt auto-detects resources from k8s YAML, customize naming:
def resource_name(id):
# id.name = workload name from k8s metadata
# Strip common prefixes
return id.name.removeprefix('myco-')
workload_to_resource_function(resource_name)