Skip to content

CRD Reference

DroidFarm installs three custom resources in the droidfarm.io/v1alpha1 API group.

DeviceTemplate

A declarative profile that describes an Android device configuration.

apiVersion: droidfarm.io/v1alpha1
kind: DeviceTemplate
metadata:
  name: myapp-android14
  namespace: droidfarm-system
spec:
  androidVersion: "14"           # "13" | "14" | "15"
  avdProfile: pixel_6             # Any valid AVD hardware profile
  resources:
    cpu: "2"
    memory: "4Gi"
  appConfig:
    - packageName: com.example.myapp           # Must match [\w.]+
      apkSource:
        url: https://artifacts.example.com/app.apk   # Must be https://
      managedConfig:
        - key: api_endpoint
          value: https://staging.api.example.com
      permissions:
        - android.permission.CAMERA
        - android.permission.ACCESS_FINE_LOCATION
      autoLaunch: true
      clearDataBetweenSessions: true
    - packageName: com.companion.app            # Additional apps
      apkSource:
        url: https://artifacts.example.com/companion.apk
  systemConfig:
    locale: en_US
    timezone: UTC
  testConfig:
    resetStrategy: fastReset   # fastReset | fullReset

Spec fields

Field Type Required Description
androidVersion string yes Android API level image to use ("13", "14", "15")
avdProfile string yes AVD hardware profile (e.g. pixel_6, pixel_4a)
emulationBackend string no cuttlefish-qemu (default, no KVM, WebRTC built-in) or cuttlefish (KVM, faster boot)
resources.cpu string yes CPU request/limit for emulator pod
resources.memory string yes Memory request/limit for emulator pod
appConfig []AppEntry no Apps to install; first entry is the primary AUT
appConfig[].packageName string yes Android package name (validated with regex [\w.]+)
appConfig[].apkSource.url string yes HTTPS URL to APK (must start with https://)
appConfig[].managedConfig []KV no Android Enterprise managed app config key/value pairs
appConfig[].permissions []string no Permissions to grant via ADB
appConfig[].autoLaunch bool no Launch app on device ready
appConfig[].clearDataBetweenSessions bool no adb shell pm clear before each session
systemConfig.locale string no Device locale (default: en_US)
systemConfig.timezone string no Device timezone (default: UTC)
testConfig.resetStrategy string no fastReset (clear data) or fullReset (wipe and reinstall)

DevicePool

A fleet of emulators backed by a DeviceTemplate.

apiVersion: droidfarm.io/v1alpha1
kind: DevicePool
metadata:
  name: staging-pool
  namespace: droidfarm-system
spec:
  templateRef:
    name: myapp-android14
  replicas:
    min: 1
    max: 8
  sessionPolicy:
    timeout: 30m
    recycleAfterSessions: 10
  streaming:
    enabled: true
    domain: local.geekxflood.io
    # The defaults below target the cluster's shared Cilium Gateway.
    # Override only when running a dedicated streaming Gateway.
    # gatewayName: cilium-gateway
    # gatewayNamespace: kube-system
    # gatewaySectionName: https
  appium:
    enabled: true

Spec fields

Field Type Required Default Description
templateRef.name string yes Name of DeviceTemplate in the same namespace
replicas.min int yes Minimum devices always running
replicas.max int yes Maximum devices (KEDA scales up to this)
sessionPolicy.timeout duration no 30m Maximum session duration
sessionPolicy.recycleAfterSessions int no Replace pod after N sessions
nodeSelector map no Optional. Add kvm: "true" only when using emulationBackend: cuttlefish.
streaming.enabled bool no true Create per-device HTTPRoute objects
streaming.domain string no Base domain; per-device hostnames are <pool>-<ordinal>.<domain>
streaming.gatewayName string no cilium-gateway Name of the Gateway the HTTPRoutes attach to
streaming.gatewayNamespace string no kube-system Namespace of the Gateway
streaming.gatewaySectionName string no https Listener section on the Gateway
appium.enabled bool no Start Appium server sidecar

The streaming defaults target the cluster's shared Cilium Gateway (kube-system/cilium-gateway, listener section https) — the Cilium 1.18+ cluster-wide Gateway pattern. The Gateway's https listener must carry a wildcard certificate that covers *.<streaming.domain> and its LoadBalancer address must resolve for that wildcard. Override gatewayName / gatewayNamespace / gatewaySectionName only when attaching the pool to a dedicated streaming Gateway — see streaming.gateway chart values.

Status fields

Field Description
status.readyReplicas Devices in Idle state
status.devices Per-device state map (Booting, Idle, Busy)
conditions Standard Kubernetes conditions

TestSession

A single E2E test run that claims an Idle device from a pool.

apiVersion: droidfarm.io/v1alpha1
kind: TestSession
metadata:
  generateName: myapp-e2e-
  namespace: droidfarm-system
spec:
  poolRef:
    name: staging-pool
  timeout: 45m
  appium:
    configMapRef:
      name: myapp-appium-caps
  artifacts:
    recording: true
    s3:
      bucket: droidfarm-results
      prefix: runs/

Spec fields

Field Type Required Description
poolRef.name string yes DevicePool to claim a device from
timeout duration no Hard timeout; session is terminated after this (default: 30m)
appium.configMapRef.name string no ConfigMap holding Appium desired capabilities
artifacts.recording bool no Enable screen recording
artifacts.s3.bucket string no S3 bucket for artifacts
artifacts.s3.prefix string no S3 key prefix

Status fields

Field Description
status.phase Pending, Claiming, Preparing, Running, Collecting, Succeeded, Failed, TimedOut
status.result Pass, Fail, Error, Skipped — set by the external runner or operator
status.appiumEndpoint WebDriver URL: http://<pod-name>.droidfarm-system.svc:4723
status.streamURL Browser stream URL: https://<pool>-<ordinal>.<streaming.domain> — served by the cluster's shared Cilium Gateway (kube-system/cilium-gateway, https listener) via the per-device HTTPRoute
status.deviceRef Pod name of the claimed device
status.startTime When the session entered Running
status.endTime When the session reached a terminal phase
status.duration Human-readable session duration (set after terminal phase)
status.artifactURL URL to uploaded recordings/results (set after Collecting)
status.failureMessage Human-readable failure reason (set on Failed or TimedOut)