Path 0 — Local k3d¶
Local deployment using k3d. No external registry required. Best for development, learning, and CI pipelines.
When to use¶
- First-time deployment of a blueprint
- Development and iteration on Section 1 business logic
- CI/CD pipelines where you want a clean Kubernetes environment per run
- Testing before promoting to cloud
Prerequisites¶
- Docker Desktop (running)
- k3d >= 5.0 (
k3d versionto check) - kubectl
k3d version requirement
k3d versions below 5.0 do not support host.k3d.internal DNS. Cross-cluster blueprints will fail with a DNS resolution error on older versions.
Single-cluster deployment¶
1. Create the cluster¶
Blueprints use specific NodePort values pre-configured in the manifests. Create your cluster with matching port mappings:
k3d cluster create wosp-cluster \
--port "30011:30011@loadbalancer" \
--port "30012:30012@loadbalancer" \
--agents 1
Check the cluster is ready:
2. Build and import images¶
For each pod in the blueprint, build and import the app image:
cd gateway/app/
docker build -t serial-app-wosp-node:latest .
k3d image import serial-app-wosp-node:latest
Repeat for every pod directory. The tag serial-app-wosp-node:latest is referenced by imagePullPolicy: Never in the deployment manifests — use this exact tag unless you update the YAML.
3. Deploy¶
Wait for completion before verifying. deploy.sh applies manifests in order and waits for pod readiness.
4. Verify¶
Check all pods are running with 3/3 containers:
Each blueprint pod should show 3/3 Ready. The three containers are web-app, xtra-wasm, and web-retriever.
Check the auto-trigger on the gateway pod:
Expected: 🔁 Auto-trigger complete — 5/5 messages sent.
Multi-cluster k3d deployment¶
Cross-cluster blueprints (Fast International Ferry, International Ferry) require two k3d clusters that can reach each other.
The isolation problem¶
Two separate k3d clusters run in separate Docker bridge networks by default and cannot reach each other's IPs directly.
The solution: host.k3d.internal + port mappings¶
k3d >= 5.0 provides host.k3d.internal as a DNS name that resolves to the Docker host IP from inside any container. Combined with --port flags that map host ports to cluster LoadBalancers, pods in different clusters can communicate via the host machine.
# Create both clusters with port mappings at creation time
# (Port mappings cannot be added after cluster creation)
k3d cluster create cluster-a \
--port "30100:30100@loadbalancer" \
--agents 1
k3d cluster create cluster-b \
--port "30200:30200@loadbalancer" \
--agents 1
Deploy processor cluster first¶
The processor/sink cluster must be deployed before the gateway cluster, because the gateway's configuration references the processor's LoadBalancer address.
# 1. Deploy the processor/sink cluster
kubectl config use-context k3d-cluster-b
cd terminator/app/ && docker build -t serial-app-wosp-node:latest . && k3d image import serial-app-wosp-node:latest -c cluster-b
cd ../..
bash deploy-cluster-b.sh
# 2. Deploy the gateway cluster
kubectl config use-context k3d-cluster-a
cd initiator/app/ && docker build -t serial-app-wosp-node:latest . && k3d image import serial-app-wosp-node:latest -c cluster-a
cd ../..
bash deploy-cluster-a.sh
Verify cross-cluster¶
Check the gateway logs for the auto-trigger completion. Then check the result trail:
The "trail" in each result should contain pod names from both clusters, confirming the cross-cluster connection is live.
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
ImagePullBackOff | Image not imported into k3d | Run k3d image import <image> -c <cluster> |
ErrImageNeverPull | Tag mismatch between import and manifest | Ensure you built with the exact tag in 03-deployment.yaml |
CrashLoopBackOff on xtra-wasm | Empty or missing Hopr credential in 02-secrets.yaml | Check all four fields: HOPR_LICENSE, HOPR_KEY, CHIPS_ALGORITHM, HOPR_API_TOKEN |
No 🔁 Auto-trigger complete in logs | Pod not yet ready, or WoSP failed to authenticate | Wait 60s after kubectl apply, then check xtra-wasm logs |
Connection refused on host.k3d.internal | Port not mapped at cluster creation | Delete cluster and recreate with correct --port flags |
| DNS failure on cross-cluster blueprint | k3d < 5.0 | Upgrade k3d to >= 5.0 |
CYCLE COMPLETE missing in trail | Cross-cluster routing misconfigured | Verify host.k3d.internal resolves inside the pod and port is mapped |
Teardown¶
To delete the cluster entirely: