Skip to main content
Tilt provides a powerful local Kubernetes development experience with live updates, resource visualization, and integrated observability. This setup uses Kind (Kubernetes in Docker) for the cluster and Tilt for the development workflow.

Why Kubernetes + Tilt?

Compared to Docker Compose, this setup offers:
  • Production parity: Run the same manifests used in production
  • Observability: Prometheus, Grafana, Loki, and Tempo for metrics, logs, and traces
  • Network visibility: Hubble UI for visualizing service mesh traffic
  • Live updates: Fast rebuild and redeploy on code changes
  • Resource dashboard: Visual monitoring of all services in one place
This setup requires significantly more resources than Docker Compose. Recommended minimum: 16GB RAM, 4 CPU cores.

Prerequisites

  • Environment setup complete
  • Docker Desktop or Docker Engine running
  • At least 16GB RAM and 4 CPU cores available
  • Infrastructure repository cloned

Bootstrap Options

There are two bootstrap options depending on your available resources:

Starting Tilt

Once the cluster is bootstrapped, start the application services:
1

Navigate to application repository

cd /path/to/microservice-app
2

Start Tilt

3

Monitor startup

Open http://localhost:10350 to see the Tilt dashboard.You’ll see resources building and deploying:
  • Bootstrap resources: Cluster setup, manifest generation
  • Codegen resources: Protocol Buffer compilation
  • Compile resources: Go service compilation (if not using Nix)
  • Kubernetes resources: Service deployments with port forwards
4

Access the application

Once all resources are green, access the frontend:
http://localhost:30081

Tilt Architecture

The Tiltfile orchestrates the following workflow:
┌─────────────────────────────────────────────────────┐
│                   Tilt Workflow                     │
└─────────────────────────────────────────────────────┘

1. Bootstrap Phase
   ├── cluster-up          Create Kind cluster (if needed)
   └── gen-manifests       Generate K8s manifests from nixidy

2. Code Generation Phase
   └── buf-generate        Generate Go/TypeScript from proto

3. Build Phase (if USE_NIX=false)
   ├── greeter-compile     Build Go binary
   ├── caller-compile      Build Go binary
   └── gateway-compile     Build Go binary

4. Image Phase
   ├── Go services         custom_build (Nix) or docker_build_with_restart
   ├── Node.js services    docker_build
   └── Frontend            docker_build with VITE_API_BASE_URL

5. Deploy Phase
   ├── Apply K8s manifests
   └── Set up port forwards

Resource Management

Tilt organizes resources into labeled groups:

Bootstrap Resources

  • cluster-up: Ensures Kind cluster exists
  • gen-manifests: Generates Kubernetes manifests from Nix modules

Codegen Resources

  • buf-generate: Compiles Protocol Buffers to Go and TypeScript

Compile Resources (non-Nix mode)

  • greeter-compile, caller-compile, gateway-compile: Build Go binaries

Kubernetes Resources

Each service runs as a Kubernetes Deployment:
ServicePort ForwardDependencies
traefik-gen-manifests
caller-service8081gen-manifests, buf-generate, caller-compile
greeter-service8080gen-manifests, buf-generate, greeter-compile
gateway8082gen-manifests, buf-generate, gateway-compile
custom-lang-service3000gen-manifests
auth-service8090gen-manifests
frontend5173gen-manifests, buf-generate

Live Updates

Go Services (non-Nix mode)

Tilt uses docker_build_with_restart for fast iteration:
  1. Edit Go source code
  2. Local resource compiles binary (greeter-compile)
  3. Binary is synced to running container via live_update
  4. Container restarts with new binary
Live updates skip the full Docker build, making updates nearly instant.

Go Services (Nix mode)

With USE_NIX=true, Tilt uses Nix to build reproducible images:
export USE_NIX=true
tilt up
  1. Edit Go source code
  2. Nix builds a complete container image
  3. Image is loaded into Kind cluster
  4. Kubernetes pulls the new image
Nix builds are slower but provide exact reproducibility. Use for testing production builds.

Node.js Services

Node.js services use standard docker_build:
  1. Edit Node.js source code
  2. Tilt rebuilds the Docker image
  3. Image is pushed to Kind
  4. Kubernetes redeploys the pod

Frontend

Frontend uses docker_build with build args:
docker_build(
    'frontend',
    context='.',
    dockerfile='deploy/docker/frontend/Dockerfile',
    build_args={'VITE_API_BASE_URL': 'http://localhost:30081'},
)
For faster frontend iteration, use the local dev server instead of rebuilding Docker images.

Port Forwards

Tilt automatically forwards these ports to localhost:
ServiceLocal PortPod Port
greeter-service80808080
caller-service80818081
gateway80828082
custom-lang-service30003000
auth-service80908090
frontend517380
Additional infrastructure ports (from full bootstrap):
ServiceLocal Port
Traefik30081
Grafana30300
Prometheus30090
Hubble UI31235

Environment Variables

Tilt honors these environment variables:

USE_NIX

Default: false Use Nix to build container images for Go services:
export USE_NIX=true
tilt up

TILT_SKIP_CLUSTER_UP

Default: false Skip the cluster-up resource if you’ve already created the cluster manually:
export TILT_SKIP_CLUSTER_UP=true
tilt up

Debugging

Check resource status

In the Tilt UI (http://localhost:10350):
  • Green: Resource is healthy
  • Yellow: Resource is building/updating
  • Red: Resource failed
Click any resource to view logs and details.

Manual health check

Tilt includes a manual health check resource:
  1. Open Tilt UI
  2. Find health-check in the resource list
  3. Click the trigger button
This runs:
kubectl cluster-info
kubectl get pods -A
kubectl get svc -A
curl http://localhost:8080/healthz   # greeter
curl http://localhost:8082/healthz   # gateway
curl http://localhost:5173           # frontend
curl http://localhost:30081          # traefik

Debug Kubernetes directly

Use the debug-k8s command from devenv:
debug-k8s
This shows pod status and recent events across all namespaces.

Debug gRPC services

debug-grpc
Tests greeter and gateway endpoints with grpcurl.

View logs

# Click on any resource in http://localhost:10350

Common Operations

Restart a service

In Tilt UI:
  1. Find the service resource
  2. Click the ⟳ (restart) button
Or via CLI:
kubectl rollout restart deployment/greeter-service

Trigger a rebuild

In Tilt UI:
  1. Find the service resource
  2. Click the ⚡ (rebuild) button
Or edit source code—Tilt auto-detects changes.

Stop Tilt

Press q in the terminal running tilt up.

Destroy the cluster

kind delete cluster --name microservice-app
This deletes all Kubernetes resources and requires re-running bootstrap.

Updating Manifests

Kubernetes manifests are generated from Nix modules in deploy/nixidy/.

Edit manifest configuration

  1. Edit files in deploy/nixidy/env/ or deploy/k8s/
  2. Regenerate manifests:
    gen-manifests
    
  3. Tilt auto-detects changes and reapplies
Or use the watch mode:
watch-manifests
This watches nixidy files and auto-applies changes to the cluster.

Observability (Full Bootstrap Only)

Grafana Dashboards

Access Grafana at http://localhost:30300 (admin/admin). Pre-configured dashboards:
  • Kubernetes cluster metrics: Node and pod resource usage
  • Service metrics: Request rates, latencies, error rates
  • Logs (Loki): Aggregated logs from all services
  • Traces (Tempo): Distributed tracing for requests

Prometheus

Query metrics at http://localhost:30090. Example queries:
# Request rate per service
rate(http_requests_total[5m])

# Pod CPU usage
sum(rate(container_cpu_usage_seconds_total[1m])) by (pod)

Hubble UI

Visualize service mesh traffic at http://localhost:31235.
  • View real-time network flows
  • Inspect HTTP/gRPC requests
  • Debug connectivity issues

Performance Tips

Reduce resource usage

  1. Use lightweight bootstrap instead of full
  2. Disable unused services in Tiltfile
  3. Limit Kind cluster resources in kind-config.yaml

Speed up builds

  1. Use USE_NIX=false for faster Go builds
  2. Leverage Docker build cache (avoid --no-cache)
  3. Use live_update for instant Go binary sync

Optimize dependencies

In Tiltfile, carefully manage resource_deps to avoid unnecessary rebuilds:
# Good: Only rebuild when dependencies change
k8s_resource('greeter-service', resource_deps=['buf-generate', 'greeter-compile'])

# Bad: Rebuilds on every manifest change
k8s_resource('greeter-service', resource_deps=['gen-manifests'])

Troubleshooting

Tilt won’t start

Check cluster:
kubectl cluster-info
If cluster isn’t running:
cd microservices-infra
full-bootstrap  # or bootstrap

Resource stuck in yellow/red

  1. Click the resource in Tilt UI
  2. Check logs for errors
  3. Common fixes:
    • Run buf generate if proto changes aren’t detected
    • Run gen-manifests if Nix changes aren’t applied
    • Restart Tilt: tilt down && tilt up

Port forward failures

If a port is already in use:
# Find what's using the port
lsof -i :8080

# Kill the process or change port in Tiltfile

Manifests not found

If you see “No Kubernetes manifests found yet”:
# Ensure nixidy modules are committed to git
git add deploy/nixidy deploy/k8s

# Regenerate manifests
gen-manifests

# Restart Tilt
tilt down && tilt up
Nix only sees files tracked by git. Always git add new files before running gen-manifests.

Image pull errors

If Kubernetes can’t pull images:
# Load images manually
load-microservice-images

# Or rebuild in Tilt UI

Next Steps

Frontend Development

Fast iteration with local dev server

Docker Compose

Simpler alternative for basic development

Build docs developers (and LLMs) love