Quick Start¶
This guide gets you from zero to a running Android emulator in a Kubernetes cluster. No KVM nodes, no kernel modules, no node preparation required.
Prerequisites¶
- Kubernetes 1.28+
kubectlandhelm(3.x) in your PATH- TURN server credentials (you can generate random ones for internal clusters)
Cilium prerequisites¶
DroidFarm's per-device streaming pipeline depends on two Cilium Gateway-API flags that ship as false by default and that a Cilium Helm upgrade at the cluster level resets back to false every time it runs:
enable-gateway-api-alpn— without this, the TLS listener does not advertise ALPNh2, browsers fall back to HTTP/1.1, and Cilium strips the gRPC-Web trailer frame. The WebRTC UI stays on "connecting" forever.enable-gateway-api-app-protocol— without this, Cilium ignores the per-device Service'sappProtocol: kubernetes.io/h2cand speaks HTTP/1.1 to the in-pod envoy sidecar. The emulator's gRPC backend is unreachable.
Run the helper script before installing DroidFarm:
It patches kube-system/cilium-config, restarts cilium-operator and the cilium DaemonSet, and is safe to re-run (idempotent — it skips if both flags are already true).
Re-run this script after every Cilium Helm upgrade at the cluster level — or bake the same two values into your Cilium Helm values overlay so they survive upgrades automatically.
Step 1 — Add the Helm repo¶
Step 2 — Deploy DroidFarm¶
helm install droidfarm droidfarm/droidfarm \
--namespace droidfarm-system \
--create-namespace \
--set turn.credentials.username=myuser \
--set turn.credentials.password=mypass
No node preparation needed. The
cuttlefish-qemubackend runs the Android device using QEMU software emulation inside the container. WebRTC is built-in at port 8443. Any standard Kubernetes node works.
Verify all pods are running:
Expected output:
NAME READY STATUS RESTARTS AGE
droidfarm-operator-6b9d4f8b5c-x7kp 1/1 Running 0 30s
droidfarm-dashboard-7d6c9f4b9d-q2lm 1/1 Running 0 30s
droidfarm-coturn-5f8c6b7d9f-p8nw 1/1 Running 0 30s
Step 3 — Create a DeviceTemplate¶
A DeviceTemplate is the device profile — Android version, hardware profile, APKs to install, permissions, and system settings.
Or create one inline:
kubectl apply -f - <<EOF
apiVersion: droidfarm.io/v1alpha1
kind: DeviceTemplate
metadata:
name: android14-basic
namespace: droidfarm-system
spec:
androidVersion: "14"
emulationBackend: cuttlefish-qemu # no KVM required
avdProfile: pixel_6
resources:
cpu: "2"
memory: "4Gi"
appConfig:
- packageName: com.example.myapp
apkSource:
url: https://artifacts.example.com/app.apk
autoLaunch: true
clearDataBetweenSessions: true
EOF
Expect a boot time of approximately 5–10 minutes for the first device in the pool.
Step 4 — Create a DevicePool¶
A DevicePool defines a fleet backed by a template.
kubectl apply -f - <<EOF
apiVersion: droidfarm.io/v1alpha1
kind: DevicePool
metadata:
name: staging-pool
namespace: droidfarm-system
spec:
templateRef:
name: android14-basic
replicas:
min: 1
max: 4
streaming:
enabled: true
domain: local.geekxflood.io
EOF
The operator generates one HTTPRoute per device on the cluster's shared Cilium Gateway (kube-system/cilium-gateway, listener section https). With the snippet above, the first device's stream URL is https://staging-pool-0.local.geekxflood.io.
Watch devices come online:
Step 5 — Run a test session¶
Once the pool has at least one Idle device, create a TestSession:
Poll until it reaches Running:
Retrieve the Appium endpoint and stream URL:
kubectl get testsession <name> -n droidfarm-system \
-o jsonpath='{.status.appiumEndpoint}{"\n"}{.status.streamURL}{"\n"}'
Step 6 — Open the per-device URL¶
The streamURL printed in the previous step is a fully qualified HTTPS URL served by the cluster's shared Cilium Gateway (kube-system/cilium-gateway, listener section https) and terminated with the wildcard certificate for *.<streaming.domain>. Paste it into a browser:
The browser hits the shared Cilium Gateway over HTTP/2 (ALPN h2), the matching HTTPRoute forwards to the per-device envoy sidecar over h2c, and Cuttlefish's web UI loads. From there it negotiates WebRTC media directly with the emulator (coturn-relayed if your browser and the emulator can't reach each other directly).
If the page never loads, the most common causes are:
enable-gateway-api-alpn: trueis missing inkube-system/cilium-config— the browser drops to HTTP/1.1 and the gRPC-Web trailer is stripped on the way back.- The wildcard DNS record
*.<streaming.domain>does not point to the shared Gateway's LoadBalancer address. - The shared Cilium Gateway's wildcard TLS certificate does not cover
*.<streaming.domain>.
Step 7 — Open the dashboard¶
For quick local access:
Then open http://localhost:8080 in your browser.
To expose the dashboard through the shared Cilium Gateway instead, enable the HTTPRoute:
helm upgrade droidfarm droidfarm/droidfarm \
--namespace droidfarm-system \
--reuse-values \
--set dashboard.gateway.enabled=true \
--set dashboard.gateway.hostname=droidfarm.local.geekxflood.io
The route attaches to kube-system/cilium-gateway listener https; the shared Gateway terminates TLS via its wildcard cert, so no extra cert plumbing is required on the chart side.
Using hardware acceleration (optional)¶
If your nodes have /dev/kvm, set emulationBackend: cuttlefish for approximately 10x faster boot times (~60 s vs ~5–10 min). See Hardware Acceleration for node setup instructions.
Next steps¶
- Helm Installation Reference — all chart values
- CRD Reference — full field documentation
- Testing Guide — CI integration with Appium
- CLI Reference —
droidfarmCLI tool