Run the Ambient Code Platform locally using kind (Kubernetes in Podman/Docker) for development and testing.
Cluster Name:
ambient-localDefault Engine: Podman (useCONTAINER_ENGINE=dockerfor more stable networking on macOS)
# Start cluster (uses podman by default)
make kind-up
# In another terminal, port-forward for access
make kind-port-forward
# Run tests
make test-e2e
# Cleanup
make kind-downWith Docker:
make kind-up CONTAINER_ENGINE=docker- Podman OR Docker (more stable on macOS):
- Podman:
brew install podman && podman machine init && podman machine start - Docker: https://docs.docker.com/get-docker/
- Note: Docker is more stable for kind on macOS (Podman's port forwarding can become flaky)
- Podman:
- kind:
brew install kind - kubectl:
brew install kubectl
Verify:
# With Podman (default)
podman ps && kind --version && kubectl version --client
# With Docker
docker ps && kind --version && kubectl version --clientThe platform auto-detects your host architecture and builds native images:
- Apple Silicon (M1/M2/M3):
linux/arm64 - Intel/AMD:
linux/amd64
Verify native builds:
make check-architecture # Should show "✓ Using native architecture"Manual override (if needed):
make build-all PLATFORM=linux/arm64 # Force specific architectureCreates kind cluster and deploys platform with Quay.io images.
What it does:
- Creates minimal kind cluster (no ingress)
- Deploys platform (backend, frontend, operator, minio)
- Initializes MinIO storage
- Extracts test token to
e2e/.env.test
Access:
- Run
make kind-port-forwardin another terminal - Frontend:
http://localhost:8080 - Backend:
http://localhost:8081 - Token:
kubectl get secret test-user-token -n ambient-code -o jsonpath='{.data.token}' | base64 -d
Runs Cypress e2e tests against the cluster.
Runtime: ~20 seconds (12 tests)
Deletes the kind cluster.
Best for testing without rebuilding:
make kind-up # Deploy
make test-e2e # Test
make kind-down # CleanupBuild all components from your local source tree and deploy to kind.
Note:
LOCAL_IMAGES=truerequires Podman as the container engine. Thekind-localoverlay expects thelocalhost/image prefix that Podman uses natively.
# Build, load, and deploy in one step (requires CONTAINER_ENGINE=podman)
make kind-up LOCAL_IMAGES=true
# Combine with Vertex AI
make kind-up LOCAL_IMAGES=true LOCAL_VERTEX=trueThis builds all container images from source, loads them into the kind cluster, and deploys using the kind-local overlay (which sets imagePullPolicy: IfNotPresent).
After the initial kind-up LOCAL_IMAGES=true, use kind-rebuild to pick up code changes without recreating the cluster:
# Rebuild all components, reload into kind, restart deployments
make kind-rebuildTo rebuild a single component (faster):
# Example: backend change
make build-backend && \
kind load docker-image vteam_backend:latest --name ambient-local && \
kubectl rollout restart deployment/backend-api -n ambient-code| Component | Build target | Deployment to restart |
|---|---|---|
| Backend | make build-backend |
backend-api |
| Frontend | make build-frontend |
frontend |
| Operator | make build-operator |
agentic-operator |
| Public API | make build-public-api |
public-api |
| Runner | make build-runner |
(none -- picked up by next session) |
| State Sync | make build-state-sync |
(none -- picked up by next session) |
# Check deployment images
kubectl get deployments -n ambient-code \
-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.containers[0].image}{"\n"}{end}'
# Check runner image configured in operator
kubectl get configmap operator-config -n ambient-code \
-o jsonpath='{.data.AMBIENT_CODE_RUNNER_IMAGE}'With LOCAL_IMAGES=true, images show as localhost/vteam_*:latest (Podman prefix, no quay.io).
Best for testing without rebuilding:
make kind-up # Deploy with pre-built Quay.io images
make test-e2e # Test
make kind-down # CleanupUse Google Cloud Vertex AI instead of direct Anthropic API:
# If you already have these in .zshrc (e.g., for Claude Code CLI):
# - ANTHROPIC_VERTEX_PROJECT_ID
# - CLOUD_ML_REGION
# Just add LOCAL_VERTEX=true
make kind-up LOCAL_VERTEX=trueDefault credentials: ~/.config/gcloud/application_default_credentials.json
(Created by gcloud auth application-default login)
Service account key (Ambient Code Support team): Download
ambient-code-key.json from the "Ambient Code Support" collection in
Bitwarden.
Override credentials path:
make kind-up LOCAL_VERTEX=true GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.jsonOverride all values:
make kind-up LOCAL_VERTEX=true \
ANTHROPIC_VERTEX_PROJECT_ID=my-project \
CLOUD_ML_REGION=us-east5 \
GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.jsonReconfigure existing cluster:
# If cluster is already running, run the setup script directly
# GOOGLE_APPLICATION_CREDENTIALS must be set (not just passed to make)
export GOOGLE_APPLICATION_CREDENTIALS=~/.config/gcloud/application_default_credentials.json
./scripts/setup-vertex-kind.shCreate e2e/.env to customize the deployment:
# Copy example
cp e2e/env.example e2e/.envAvailable options:
# Enable agent testing
ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
# Override specific images (for testing custom builds)
IMAGE_BACKEND=quay.io/your-org/vteam_backend:custom-tag
IMAGE_FRONTEND=quay.io/your-org/vteam_frontend:custom-tag
IMAGE_OPERATOR=quay.io/your-org/vteam_operator:custom-tag
IMAGE_RUNNER=quay.io/your-org/vteam_claude_runner:custom-tag
IMAGE_STATE_SYNC=quay.io/your-org/vteam_state_sync:custom-tag
# Or override registry for all images
CONTAINER_REGISTRY=quay.io/your-orgApply changes:
make kind-down && make kind-upTo run interactive sessions from the UI (not just automated e2e tests), the runner needs credentials. How you set this up depends on your AI provider:
With Vertex AI (recommended): Run setup-vertex-kind.sh (see Vertex AI
above). Sessions work out of the box — the operator automatically copies the
ambient-vertex secret into each project namespace and skips ambient-runner-secrets
validation.
With a direct Anthropic API key: You must create the runner secret in each project
namespace manually (the e2e/.env ANTHROPIC_API_KEY only applies to e2e test setup):
kubectl create secret generic ambient-runner-secrets \
--from-literal=ANTHROPIC_API_KEY=sk-ant-... \
-n <your-project-namespace>For frontend-only changes, skip image rebuilds entirely. Run NextJS with hot-reload against the kind cluster backend:
# Terminal 1: port-forward the backend
kubectl port-forward svc/backend-service 8081:8080 -n ambient-code
# Terminal 2: start the frontend dev server
cd components/frontend
npm install # first time only
# Create .env.local with the test user token
# .env.local is gitignored — do NOT commit it (contains a live cluster token)
TOKEN=$(kubectl get secret test-user-token -n ambient-code \
-o jsonpath='{.data.token}' | base64 -d)
cat > .env.local <<EOF
OC_TOKEN=$TOKEN
BACKEND_URL=http://localhost:8081/api
EOF
npm run dev
# Open http://localhost:3000Every file save triggers instant hot-reload — no Docker build, no kind load, no rollout restart. See Hybrid Local Development for more details.
Symptom: Pods stuck in Pending with "Insufficient memory" events.
Cause: The single-node kind cluster has limited memory. Running all platform pods plus session runner pods can exceed it.
Fix: Scale down non-essential deployments:
# These are safe to remove for local development
kubectl scale deployment ambient-api-server ambient-api-server-db \
public-api unleash --replicas=0 -n ambient-code
# If running frontend locally, also scale down the in-cluster frontend
kubectl scale deployment frontend --replicas=0 -n ambient-code# Verify container runtime is running
podman ps # or docker ps
# Recreate cluster
make kind-down
make kind-upkubectl get pods -n ambient-code
kubectl logs -n ambient-code deployment/backend-apiSymptom: Ingress works initially, then hangs after 10-30 minutes. Cause: Podman's gvproxy port forwarding can become flaky on macOS.
Workaround - Use port-forward:
# Stop using ingress on 8080, use direct port-forward instead
kubectl port-forward -n ambient-code svc/frontend-service 18080:3000
# Update test config
cd e2e
perl -pi -e 's|http://localhost:8080|http://localhost:18080|' .env.test
# Access at http://localhost:18080Permanent fix: Use Docker instead of Podman on macOS:
# Switch to Docker
make kind-down CONTAINER_ENGINE=podman
make kind-up CONTAINER_ENGINE=docker
# Access at http://localhost (port 80)lsof -i:8080 # Find what's using the port
# Kill it or edit e2e/scripts/setup-kind.sh to use different portsSymptom: qemu: uncaught target signal 11 (Segmentation fault) during Next.js build
Fix:
# Auto-detect and use native architecture
make local-clean
make local-upDiagnosis: Run make check-architecture to verify native builds are enabled.
cd e2e && ./scripts/init-minio.sh# View logs
kubectl logs -n ambient-code -l app=backend-api -f
# Restart component
kubectl rollout restart -n ambient-code deployment/backend-api
# List sessions
kubectl get agenticsessions -A
# Delete cluster
make kind-down- Hybrid Local Development - Run components locally (faster iteration)
- E2E Testing Guide - Running e2e tests
- Testing Strategy - Overview
- kind Documentation