What's Actually Happening

Tilt's live update feature is not updating running containers when files change. Code changes are not reflected in the running application.

The Error You'll See

```bash $ tilt up

# File changes detected but not applied: [demo] Build Failed: failed to sync files ```

Sync error:

bash
Error: no sync or run_in_container specified for live_update

Container error:

bash
Error: container not found: app

Permission error:

bash
Error: permission denied: /app/src

Why This Happens

  1. 1.Live_update not configured - Missing live_update section in Tiltfile
  2. 2.Sync paths wrong - Source paths don't match container paths
  3. 3.Container name wrong - fall_back_on or container specified incorrectly
  4. 4.Dockerfile incompatible - Dockerfile doesn't support file updates
  5. 5.Permission issues - Container user can't write to synced directories
  6. 6.Process not reloading - Application needs restart after file changes

Step 1: Check Tiltfile Configuration

```python # View Tiltfile: cat Tiltfile

# Check live_update configuration: cat Tiltfile | grep -A10 "live_update"

# Basic live_update syntax: docker_build( 'my-image', '.', live_update=[ sync('./src:/app/src'), run('cd /app && npm install', trigger='./package.json'), ] )

# Check if docker_build has live_update: docker_build( 'my-image', '.', # Missing live_update = full rebuild only )

# Add live_update: docker_build( 'my-image', '.', live_update=[ sync('./src:/app/src'), ] )

# Validate Tiltfile: tilt ci

# Check Tiltfile syntax: tilt dump > tilt-config.json cat tilt-config.json | jq '.' ```

Step 2: Verify Sync Paths

```python # Check sync paths: # Format: sync('local_path:container_path')

# Verify local path exists: ls ./src

# Check container path in Dockerfile: cat Dockerfile | grep WORKDIR cat Dockerfile | grep COPY

# Common sync configurations: # For Node.js: sync('./src:/app/src')

# For Python: sync('./app:/app/app')

# For Go: sync('./cmd:/app/cmd') sync('./pkg:/app/pkg')

# Multiple syncs: live_update=[ sync('./src:/app/src'), sync('./public:/app/public'), ]

# Check sync in Tilt UI: # Open Tilt UI -> Click resource -> View "Build History" # Look for "Live Update" in build log

# Test sync manually: docker exec -it <container> ls /app/src ```

Step 3: Add run Commands

```python # Add run_in_container for updates: docker_build( 'my-image', '.', live_update=[ sync('./src:/app/src'), run('pip install -r requirements.txt', trigger='./requirements.txt'), ] )

# Run on container: # run() - runs in container after sync # run_local() - runs on host machine

# Restart application: docker_build( 'my-image', '.', live_update=[ sync('./src:/app/src'), run('pkill -f "python" || true'), # Kill process run('python /app/main.py &'), # Restart ] )

# Use trigger for selective updates: live_update=[ sync('./src:/app/src'), run('npm install', trigger='./package.json'), # Only on package.json change run('npm run build', trigger='./src/**/*.ts'), # Only on TS file changes ]

# Fall back on full rebuild: docker_build( 'my-image', '.', live_update=[ sync('./src:/app/src'), ], match_in_env_vars=True, # Check env vars for image name ) ```

Step 4: Fix Container Permissions

```bash # Check container user: docker exec -it <container> whoami

# Check file permissions in container: docker exec -it <container> ls -la /app

# If permission denied, fix in Dockerfile: # Option 1: Run as root USER root

# Option 2: Fix directory permissions RUN chown -R app:app /app

# Option 3: Use chmod RUN chmod -R 755 /app

# Check if container has write access: docker exec -it <container> touch /app/src/test.txt

# Fix with run command: live_update=[ sync('./src:/app/src'), run('chown -R app:app /app/src || true'), ]

# Check container user in Dockerfile: cat Dockerfile | grep USER ```

Step 5: Configure Fallback Strategy

```python # Set fallback_on for rebuild triggers: docker_build( 'my-image', '.', live_update=[ sync('./src:/app/src'), ], fallback_on=[ './requirements.txt', # Full rebuild on requirements change './Dockerfile', # Full rebuild on Dockerfile change ] )

# Only build on changes: docker_build( 'my-image', '.', only=['./src', './package.json'], # Only watch these paths live_update=[ sync('./src:/app/src'), ] )

# Ignore files: docker_build( 'my-image', '.', ignore=['./node_modules', './.git'], live_update=[ sync('./src:/app/src'), ] )

# Set container target for live_update: docker_build( 'my-image', '.', live_update=[ sync('./src:/app/src'), ], target='development', # Build this Dockerfile stage ) ```

Step 6: Use k8s_yaml and k8s_resource

```python # Check resource configuration: cat Tiltfile | grep -A5 "k8s_resource"

# Associate image with resource: docker_build('my-image', '.', live_update=[sync('./src:/app/src')])

k8s_yaml('kubernetes.yaml')

k8s_resource( 'my-app', port_forwards=8080, extra_pod_selectors=[{'app': 'my-app'}] )

# Set resource name: k8s_resource( 'my-app', new_name='demo', # Rename in Tilt UI port_forwards=8080, )

# Multiple resources: k8s_resource('app1', port_forwards=8080) k8s_resource('app2', port_forwards=8081)

# Check resource pod: tilt get pods

# View resource status: tilt get resources ```

Step 7: Debug Live Update

```bash # Enable verbose logging: tilt up --verbose

# Check Tilt logs: tilt logs

# View Tilt UI: # Open http://localhost:10350

# Check file watcher: # In Tiltfile, add: watch_file('./config.yaml')

# Trigger manual rebuild: tilt trigger <resource-name>

# View build history: # In Tilt UI -> Resource -> Build History

# Check for file sync errors: tilt logs | grep -i "sync|update|error"

# Test container update manually: docker cp ./src/file.py <container>:/app/src/file.py docker exec <container> cat /app/src/file.py ```

Step 8: Fix Process Reload

```python # Applications may need restart after file sync:

# Python with gunicorn: live_update=[ sync('./src:/app/src'), run('kill -HUP 1'), # Signal gunicorn to reload ]

# Node.js with nodemon: # Use nodemon in Dockerfile: CMD ["nodemon", "index.js"] # Files sync, nodemon auto-reloads

# Go with CompileDaemon: # In Dockerfile: CMD ["CompileDaemon", "-build=go build -o app .", "-command=./app"] # Or use air for hot reload: CMD ["air"]

# Java with Spring Boot DevTools: # Add dependency and: live_update=[ sync('./src:/app/src'), run('./mvnw compile'), ]

# Generic restart: live_update=[ sync('./src:/app/src'), run('pkill -f "main.py" || true'), run('python /app/main.py &'), ] ```

Step 9: Check Dockerfile Compatibility

```dockerfile # Check Dockerfile for live_update compatibility:

# Bad: Entrypoint blocks sync ENTRYPOINT ["./start.sh"]

# Good: CMD can be overridden CMD ["python", "main.py"]

# Bad: Single layer COPY . /app RUN pip install -r requirements.txt && pip cache purge

# Good: Separate layers COPY requirements.txt /app/ RUN pip install -r requirements.txt COPY . /app

# Use multi-stage for smaller updates: FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build

FROM node:18 AS runtime WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules CMD ["node", "dist/main.js"]

# Target development stage: docker_build( 'my-image', '.', target='development', # Use development stage live_update=[ sync('./src:/app/src'), ] ) ```

Step 10: Tilt Verification Script

```bash # Create verification script: cat << 'EOF' > /usr/local/bin/check-tilt-live-update.sh #!/bin/bash

echo "=== Tiltfile Content ===" cat Tiltfile

echo "" echo "=== Docker Build Config ===" grep -A20 "docker_build" Tiltfile

echo "" echo "=== Live Update Config ===" grep -A10 "live_update" Tiltfile

echo "" echo "=== Dockerfile WORKDIR ===" grep -i "WORKDIR|COPY|CMD" Dockerfile

echo "" echo "=== Current Directory ===" ls -la

echo "" echo "=== Source Files ===" ls -la ./src 2>/dev/null || ls -la ./app 2>/dev/null || echo "No src or app directory"

echo "" echo "=== Tilt Status ===" tilt get resources 2>/dev/null || echo "Tilt not running"

echo "" echo "=== Docker Containers ===" docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"

echo "" echo "=== Test File Sync ===" touch ./src/.tilt-test-$(date +%s) echo "Created test file, check Tilt UI for sync"

echo "" echo "=== Recommendations ===" echo "1. Add live_update to docker_build in Tiltfile" echo "2. Verify sync paths match container paths" echo "3. Check container has write permissions" echo "4. Add run commands to restart application" echo "5. Use fallback_on for dependency changes" echo "6. Ensure Dockerfile supports file updates" echo "7. Check application reload mechanism" EOF

chmod +x /usr/local/bin/check-tilt-live-update.sh

# Usage: /usr/local/bin/check-tilt-live-update.sh ```

Tilt Live Update Checklist

CheckExpected
live_update definedIn docker_build
Sync paths correctLocal:Container match
Container permissionsWrite access to sync dir
Process reloadApp restarts on change
Fallback configuredFull rebuild on key changes
Dockerfile compatibleCMD not blocking
Files monitoredTilt sees file changes

Verify the Fix

```bash # After fixing Tilt live update

# 1. Start Tilt tilt up // Tilt UI opens

# 2. Make file change echo "// test" >> src/main.py // Tilt shows live update

# 3. Check container docker exec -it <container> cat /app/src/main.py // Change reflected

# 4. View build history # In Tilt UI -> Resource -> Build History // Live Update appears

# 5. Test multiple changes # Make several file changes // Updates applied incrementally

# 6. Check application curl http://localhost:8080 // Response reflects changes ```

  • [Fix Docker Compose Up Failed](/articles/fix-docker-compose-up-failed)
  • [Fix Skaffold Build Failed](/articles/fix-skaffold-build-failed)
  • [Fix Kubernetes Deployment Not Working](/articles/fix-kubernetes-deployment-not-working)