Hybrid Local Development
March 4, 2026 ยท View on GitHub
Run components locally (outside cluster) while using kind for dependencies. Fastest iteration cycle.
Overview
Choose which components to run locally based on what you're developing:
| Scenario | Local | In Cluster | Port-Forward | Best For |
|---|---|---|---|---|
| Frontend Only | Frontend | Backend, Operator, MinIO | Backend โ 8090 | UI/UX work |
| Frontend + Backend | Frontend, Backend | Operator, MinIO | None | API development |
| Full Stack | Frontend, Backend, Operator | MinIO only | None | Operator work |
Benefits:
- โก Instant reloads (no image build/push)
- ๐ Better debugging (direct logs, breakpoints)
- ๐ Faster iteration (seconds vs minutes)
Scenario 1: Frontend Only
Best for: UI/UX work, React components, styling
Run Next.js dev server locally, connect to backend in cluster via port-forward.
Frontend (localhost:3000) โ Backend (cluster:8090) โ K8s API
Setup
Terminal 1 - Port-forward backend:
# Forward backend service to localhost:8090
kubectl port-forward -n ambient-code svc/backend-service 8090:8080
Terminal 2 - Run frontend:
cd components/frontend
# Set backend URL to port-forwarded backend
export BACKEND_URL=http://localhost:8090/api
# Run dev server
npm run dev
# Access at http://localhost:3000
What's Happening
- Frontend talks to backend via port-forward tunnel
- Backend runs in cluster, has full K8s access
- Operator in cluster handles sessions
Fast Iteration
- Edit React components โ instant hot reload
- Edit styles โ instant update
- No backend restarts needed
Scenario 2: Frontend + Backend
Best for: Backend API work, handler logic, new endpoints
Run frontend and backend locally, operator stays in cluster.
Frontend (localhost:3000) โ Backend (localhost:8090) โ K8s API (via KUBECONFIG)
โ
Operator (cluster) watches CRs
Setup
One-time: Create minimal cluster
# Start kind, scale down components we'll run locally
make kind-up
kubectl scale -n ambient-code deployment/backend-api deployment/frontend --replicas=0
Terminal 1 - Backend:
cd components/backend
export KUBECONFIG=~/.kube/config # Direct K8s API access
export PORT=8090
go run .
Terminal 2 - Frontend:
cd components/frontend
export BACKEND_URL=http://localhost:8090/api
npm run dev
# Access at http://localhost:3000
What's Happening
- No port-forwarding needed!
- Backend uses
KUBECONFIGto talk directly to K8s API - Backend creates/reads CRs, operator in cluster reacts to them
- Frontend talks to local backend
Fast Iteration
- Edit backend code โ restart (few seconds)
- Edit frontend code โ instant hot reload
- See logs directly in terminal
- Full debugging with breakpoints
Scenario 3: Full Local Stack
Best for: Operator development, reconciliation logic, full integration testing
Run everything locally except MinIO and runner jobs.
Frontend (localhost:3000) โ Backend (localhost:8090) โ K8s API (via KUBECONFIG)
โ
Operator (localhost) โ K8s API (via KUBECONFIG)
โ
Creates runner jobs in cluster
Setup
One-time: Create minimal cluster
# Start kind, scale down all components we'll run locally
make kind-up
kubectl scale -n ambient-code deployment/backend-api deployment/frontend deployment/agentic-operator --replicas=0
Terminal 1 - Operator:
cd components/operator
export KUBECONFIG=~/.kube/config
export AMBIENT_CODE_RUNNER_IMAGE=quay.io/ambient_code/vteam_claude_runner:latest
export STATE_SYNC_IMAGE=quay.io/ambient_code/vteam_state_sync:latest
go run .
Terminal 2 - Backend:
cd components/backend
export KUBECONFIG=~/.kube/config
export PORT=8090
go run .
Terminal 3 - Frontend:
cd components/frontend
export BACKEND_URL=http://localhost:8090/api
npm run dev
# Access at http://localhost:3000
What's Happening
- No port-forwarding needed!
- All components use
KUBECONFIGfor direct K8s API access - Operator watches for CR changes, creates runner jobs in cluster
- MinIO stays in cluster (for session state storage)
- Runner jobs still run as pods (containerized execution)
Fast Iteration
- Edit operator code โ restart (~10 seconds)
- Edit backend code โ restart (~5 seconds)
- Edit frontend code โ instant hot reload
- See all logs in separate terminals
- Full debugging across entire stack
VS Code Tasks
We've created VS Code tasks for quick access:
Kind Cluster:
Kind: Start Cluster- Create kind cluster with all componentsKind: Stop Cluster- Delete kind clusterKind: Port-Forward Backend- Forward backend to localhost:8090Kind: Port-Forward Frontend- Forward frontend to localhost:3000
Hybrid Development:
Hybrid: Frontend Only- Run frontend + port-forward backendHybrid: Frontend + Backend- Run frontend + backend locallyHybrid: Full Local Stack- Run all three locally
Access via Cmd+Shift+P โ "Tasks: Run Task"
Understanding KUBECONFIG vs Port-Forwarding
Common confusion: Many think export KUBECONFIG=~/.kube/config is port-forwarding. It's not!
KUBECONFIG:
- Gives your local Go processes direct access to the Kubernetes API
- They can create/read CRs, pods, secrets, deployments, etc.
- This is why backend and operator don't need port-forwarding
Port-forwarding (kubectl port-forward):
- Tunnels traffic to a service running inside the cluster
- Only needed when you want to access a service's HTTP endpoint from localhost
- Example: Frontend needs to call backend API running in cluster
When you need port-forwarding:
- โ Scenario 1 (Frontend Only) - frontend needs to reach backend service in cluster
- โ Scenario 2 (Frontend + Backend) - backend runs locally, frontend talks to localhost
- โ Scenario 3 (Full Stack) - everything local, no services in cluster to reach
Tips & Troubleshooting
Required Environment Variables
Frontend:
BACKEND_URL=http://localhost:8090/api- Backend URL for Next.js server-side routesNEXT_PUBLIC_API_BASE_URL=/api- Client-side API base (use/apifor Next.js proxy)
Backend:
KUBECONFIG=~/.kube/config- Path to kubeconfig (for K8s API access)PORT=8090- Server port (avoid 8080 conflict with ingress)
Operator:
KUBECONFIG=~/.kube/config- Path to kubeconfigAMBIENT_CODE_RUNNER_IMAGE- Runner image (e.g.,quay.io/ambient_code/vteam_claude_runner:latest)STATE_SYNC_IMAGE- State-sync image (e.g.,quay.io/ambient_code/vteam_state_sync:latest)
Debugging
Local processes are much easier to debug:
- VS Code Go Debugger: Set breakpoints in backend/operator code
- Browser DevTools: Full React component inspection, network tab
- Direct logs: See logs in terminal, no
kubectl logsneeded - Fast iteration: Change code โ see results in seconds
Common Issues
Backend can't connect to K8s API:
# Verify KUBECONFIG is set and valid
echo $KUBECONFIG
kubectl get pods -n ambient-code
Frontend can't reach backend:
# Scenario 1: Check port-forward is running
lsof -i:8090
# Scenario 2/3: Check backend is running locally
curl http://localhost:8090/health
Operator not creating jobs:
# Check operator is running and watching
# Should see logs about watching AgenticSessions
# Check CRDs exist
kubectl get crd agenticsessions.vteam.ambient-code
When to Use Each Scenario
| Task | Recommended Scenario | Why |
|---|---|---|
| UI/UX changes | Frontend Only | Fastest - only need frontend hot reload |
| New API endpoint | Frontend + Backend | Test backend logic with fast restarts |
| Handler debugging | Frontend + Backend | Set breakpoints in backend code |
| Operator reconciliation | Full Stack | See operator logs directly |
| Integration testing | Full Kind Cluster | Test real container behavior |
| E2E testing | Full Kind Cluster | Run Cypress tests |
General rule: Run the minimum number of components locally that you need to work on.
See Also
- Kind Local Dev - Full cluster in kind
- VS Code Tasks - Quick access to dev commands
- Testing Strategy - E2E testing