Core Concepts · Additional notes

1 min read
Mid-level2 min read
Rapid overview

Additional notes

What is Tilt?

Tilt is a local Kubernetes development tool that watches your files, rebuilds containers, and updates your cluster in real-time. It uses a Tiltfile (written in Starlark, a Python dialect) to define your development environment.


Docker Build Strategies

Basic Build

docker_build(
    'api-server',           # Image name
    '.',                    # Build context
    dockerfile='Dockerfile',
    build_args={'VERSION': '1.0.0'},
    ignore=['./tests', './docs'],
)

Live Update (Hot Reload)

Live Update syncs file changes without full image rebuilds.

docker_build(
    'frontend',
    '.',
    live_update=[
        # Sync local changes to container
        sync('./src', '/app/src'),
        sync('./public', '/app/public'),

        # Run command after sync
        run('npm install', trigger=['./package.json']),

        # Restart process if needed
        run('kill -HUP 1', trigger=['./src/**/*.ts']),
    ]
)

Live Update Strategies

# Sync files only (for interpreted languages)
live_update=[
    sync('./src', '/app/src'),
]

# Sync and run build (for compiled languages)
live_update=[
    sync('./src', '/app/src'),
    run('go build -o /app/server ./cmd/server'),
]

# Full restart (when config changes)
live_update=[
    sync('./config', '/app/config'),
    restart_container(),
]

Docker Build with Restart

load('ext://restart_process', 'docker_build_with_restart')

docker_build_with_restart(
    'api-server',
    '.',
    entrypoint='/app/server',
    live_update=[
        sync('./src', '/app/src'),
        run('go build -o /app/server ./cmd/server', trigger=['./src/**/*.go']),
    ],
)

Kubernetes Configuration

Loading Manifests

# Single file
k8s_yaml('deploy/app.yaml')

# Multiple files
k8s_yaml(['deploy/app.yaml', 'deploy/service.yaml'])

# Glob pattern
k8s_yaml(glob('deploy/*.yaml'))

# Kustomize
k8s_yaml(kustomize('deploy/overlays/dev'))

# Helm
k8s_yaml(helm('charts/myapp', values=['values-dev.yaml']))

Resource Configuration

k8s_resource(
    'api-server',
    port_forwards=['8080:80', '9090:9090'],
    resource_deps=['database', 'redis'],
    labels=['backend'],
    trigger_mode=TRIGGER_MODE_MANUAL,  # Don't auto-update
)

k8s_resource(
    'database',
    port_forwards='5432:5432',
    labels=['infrastructure'],
)

Resource Dependencies

# API depends on database being ready
k8s_resource('api-server', resource_deps=['database'])

# Frontend depends on API
k8s_resource('frontend', resource_deps=['api-server'])

Local Resources

Running Tests

local_resource(
    'unit-tests',
    cmd='npm test',
    deps=['./src', './test'],
    labels=['tests'],
    auto_init=False,      # Don't run on startup
    trigger_mode=TRIGGER_MODE_MANUAL,
)

local_resource(
    'integration-tests',
    cmd='npm run test:integration',
    resource_deps=['api-server', 'database'],
    labels=['tests'],
)

Build Steps

# Build frontend before Docker build
local_resource(
    'frontend-build',
    cmd='npm run build',
    deps=['./src', './package.json'],
    labels=['build'],
)

# Make docker_build depend on local build
docker_build(
    'frontend',
    '.',
    dockerfile='Dockerfile.prod',
    only=['./build'],
)
k8s_resource('frontend', resource_deps=['frontend-build'])

Helm Integration

Basic Helm Deploy

k8s_yaml(helm(
    'charts/myapp',
    name='myapp',
    namespace='default',
    values=['values.yaml', 'values-dev.yaml'],
    set=[
        'image.repository=myapp',
        'replicaCount=1',
    ]
))

Helm with Image Override

docker_build('myapp', '.')

k8s_yaml(helm(
    'charts/myapp',
    set=['image.repository=myapp', 'image.tag=latest']
))

Extensions

Loading Extensions

# From tilt-extensions repo
load('ext://restart_process', 'docker_build_with_restart')
load('ext://namespace', 'namespace_create')
load('ext://secret', 'secret_from_dict')

# Use namespace extension
namespace_create('my-namespace')

# Create secrets
secret_from_dict('db-secret', 'my-namespace', inputs={
    'username': 'admin',
    'password': os.getenv('DB_PASSWORD'),
})

Common Extensions

ExtensionPurpose
restart_processRestart container process without rebuild
namespaceCreate/manage namespaces
secretCreate Kubernetes secrets
git_resourceDeploy from Git repos
helm_remoteDeploy Helm charts from repos
configmapCreate ConfigMaps from files

Multi-Service Development

Microservices Setup

# Tiltfile for microservices

# Shared configuration
default_registry('localhost:5000')

# Database (external dependency)
k8s_yaml('infra/postgres.yaml')
k8s_resource('postgres', port_forwards='5432', labels=['infrastructure'])

# Redis
k8s_yaml('infra/redis.yaml')
k8s_resource('redis', port_forwards='6379', labels=['infrastructure'])

# API Gateway
docker_build('api-gateway', './services/gateway')
k8s_yaml('services/gateway/k8s.yaml')
k8s_resource('api-gateway',
    port_forwards='8080',
    resource_deps=['postgres', 'redis'],
    labels=['services'])

# User Service
docker_build('user-service', './services/user',
    live_update=[
        sync('./services/user/src', '/app/src'),
    ])
k8s_yaml('services/user/k8s.yaml')
k8s_resource('user-service',
    resource_deps=['postgres'],
    labels=['services'])

# Order Service
docker_build('order-service', './services/order')
k8s_yaml('services/order/k8s.yaml')
k8s_resource('order-service',
    resource_deps=['postgres', 'redis'],
    labels=['services'])

Shared Libraries

# Watch shared library and rebuild dependents
watch_file('./shared')

docker_build('service-a', './services/a',
    deps=['./shared'])
docker_build('service-b', './services/b',
    deps=['./shared'])

Advanced Patterns

Conditional Configuration

# Development vs staging
config.define_string('environment', args=True)
cfg = config.parse()
env = cfg.get('environment', 'dev')

if env == 'dev':
    k8s_yaml(kustomize('deploy/overlays/dev'))
    allow_k8s_contexts('docker-desktop')
else:
    k8s_yaml(kustomize('deploy/overlays/staging'))
    allow_k8s_contexts('staging-cluster')

Custom Buttons

k8s_resource('api-server',
    port_forwards='8080',
    links=[
        link('http://localhost:8080/swagger', 'Swagger UI'),
        link('http://localhost:8080/health', 'Health Check'),
    ],
)

# Custom action button
local_resource(
    'seed-database',
    cmd='./scripts/seed.sh',
    auto_init=False,
    trigger_mode=TRIGGER_MODE_MANUAL,
)

Resource Groups

# Use labels to organize in UI
k8s_resource('frontend', labels=['frontend'])
k8s_resource('api-server', labels=['backend'])
k8s_resource('worker', labels=['backend'])
k8s_resource('postgres', labels=['database'])
k8s_resource('redis', labels=['cache'])

Performance Optimization

Ignore Unnecessary Files

docker_build('myapp', '.',
    ignore=[
        './node_modules',
        './.git',
        './tests',
        './docs',
        '**/*.md',
        '**/*.test.ts',
    ])

Only Include Needed Files

docker_build('frontend', '.',
    only=[
        './build',
        './package.json',
    ])

Parallel Builds

# Tilt runs builds in parallel by default
# Use resource_deps to control order when needed

docker_build('service-a', './a')  # Parallel
docker_build('service-b', './b')  # Parallel
docker_build('service-c', './c')  # Parallel

# Only service-d waits
docker_build('service-d', './d')
k8s_resource('service-d', resource_deps=['service-a', 'service-b'])

Debugging

Tilt Commands

# Start Tilt
tilt up

# Start with specific resources
tilt up frontend api-server

# Start in CI mode (exit on completion)
tilt ci

# View Tilt UI
tilt up  # Opens at http://localhost:10350

# Get resource status
tilt get
tilt describe <resource>

# Trigger manual rebuild
tilt trigger <resource>

# View logs
tilt logs <resource>

Debugging Tiltfile

# Print debug info
print("Building with config:", cfg)

# Fail with message
fail("Missing required environment variable")

# Local commands for debugging
local('kubectl get pods')

See also