Getting Started
Getting Started
This guide walks you through installing redroid-operator and running your first Android instance on Kubernetes.
Prerequisites
| Requirement | Notes |
|---|---|
| Kubernetes ≥ 1.27 | Required for spec.timeZone in CronJobs |
| Helm ≥ 3.8 | OCI chart support |
Node with /dev/kvm |
Redroid requires hardware virtualisation |
| PersistentVolume provisioner | Any RWX-capable StorageClass |
adb (Android Debug Bridge) |
On your local machine for CLI access |
Check node capabilities
# KVM must be available on the node running Redroid Pods
kubectl get nodes -o wide
ssh <node> ls /dev/kvm # should return /dev/kvm
Install the Operator
Option 1: Helm (recommended)
helm repo add redroid https://isning.github.io/redroid-operator
helm repo update
helm install redroid-operator redroid/redroid-operator \
--namespace redroid-system \
--create-namespace \
--set installCRDs=true
Verify the controller is running:
kubectl -n redroid-system get pods
# NAME READY STATUS RESTARTS AGE
# redroid-operator-controller-manager-... 1/1 Running 0 30s
Option 2: Kustomize
# Install CRDs
kubectl apply -k github.com/isning/redroid-operator/config/crd
# Deploy controller
kubectl apply -k github.com/isning/redroid-operator/config
Create Storage
Redroid uses an overlayfs model with two PVCs per node:
redroid-data-base-pvc— shared read-only lower layer (installed APKs, initial Android state)redroid-data-diff-pvc— per-instance writable upper layer (diverging state per instance)
# storage.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redroid-data-base-pvc
namespace: redroid-system
spec:
accessModes: [ReadWriteMany]
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redroid-data-diff-pvc
namespace: redroid-system
spec:
accessModes: [ReadWriteMany]
resources:
requests:
storage: 10Gi
kubectl apply -f storage.yaml
Base-Mode Bootstrap (Optional)
If you want all instances to share a common pre-installed state (apps, accounts, config), use base mode to initialise redroid-data-base-pvc first.
# base-instance.yaml
apiVersion: redroid.isning.moe/v1alpha1
kind: RedroidInstance
metadata:
name: android-base
namespace: redroid-system
spec:
index: 0
image: redroid/redroid:14.0.0-latest
sharedDataPVC: redroid-data-base-pvc
diffDataPVC: redroid-data-diff-pvc
baseMode: true # mounts sharedDataPVC as /data (read-write), skips overlayfs
kubectl apply -f base-instance.yaml
# Wait for it to be Running
kubectl -n redroid-system get redroidinstances -w
# Connect and set up (install apps, accounts, etc.)
kubectl redroid instance shell android-base -n redroid-system
# adb install myapp.apk, etc.
# Suspend when done — normal instances now inherit this state
kubectl redroid instance suspend android-base -n redroid-system
# Or delete — the shared PVC data persists
kubectl delete redroidinstance android-base -n redroid-system
Warning: Never run base-mode and normal instances concurrently against the same
sharedDataPVC— this will corrupt the overlayfs lower layer.
Create an Instance
# instance.yaml
apiVersion: redroid.isning.moe/v1alpha1
kind: RedroidInstance
metadata:
name: android-0
namespace: redroid-system
spec:
index: 0
image: redroid/redroid:14.0.0-latest
sharedDataPVC: redroid-data-base-pvc
diffDataPVC: redroid-data-diff-pvc
screen:
width: 1080
height: 1920
dpi: 480
gpuMode: host
kubectl apply -f instance.yaml
kubectl -n redroid-system get redroidinstances
# NAME INDEX SUSPEND PHASE POD ADB AGE
# android-0 0 false Running android-0-pod-xxxxx 10.96.123.45:5555 1m
Connect via ADB
Using kubectl-redroid
# Install the plugin (replace VERSION with the desired tag, e.g. v1.0.0)
VERSION=v1.0.0
curl -L "https://github.com/isning/redroid-operator/releases/download/${VERSION}/kubectl-redroid-${VERSION}-linux-amd64.tar.gz" | tar xz
sudo install kubectl-redroid /usr/local/bin/
# Or install the latest snapshot (tracks main)
curl -L https://github.com/isning/redroid-operator/releases/download/snapshot/kubectl-redroid-0.0.0-snapshot-linux-amd64.tar.gz | tar xz
sudo install kubectl-redroid /usr/local/bin/
# Port-forward and connect
kubectl redroid instance port-forward android-0 -n redroid-system
# → Forwarding localhost:5555 → android-0 (ADB)
# In a new terminal:
adb connect localhost:5555
# Or launch shell directly (handles port-forward automatically)
kubectl redroid instance shell android-0 -n redroid-system
Manual port-forward
POD=$(kubectl -n redroid-system get redroidinstance android-0 -o jsonpath='{.status.podName}')
kubectl -n redroid-system port-forward pod/${POD} 5555:5555 &
adb connect localhost:5555
adb shell
Create a Task
A RedroidTask runs a workload (sidecar container) against one or more instances. The controller injects ADB_ADDRESS automatically.
One-shot task
# task.yaml
apiVersion: redroid.isning.moe/v1alpha1
kind: RedroidTask
metadata:
name: screenshot
namespace: redroid-system
spec:
instances:
- name: android-0
suspendInstance: true # stop the instance pod while the task runs
integrations:
- name: screenshotter
image: ghcr.io/myorg/adb-tools:latest
command: [sh, -c]
args:
- |
adb -s $ADB_ADDRESS wait-for-device
adb -s $ADB_ADDRESS shell screencap -p > /output/screenshot.png
Scheduled (CronJob) task
apiVersion: redroid.isning.moe/v1alpha1
kind: RedroidTask
metadata:
name: daily-run
namespace: redroid-system
spec:
instances:
- name: android-0
- name: android-1
schedule: "0 4 * * *" # 04:00 daily
timezone: Asia/Shanghai
integrations:
- name: maa
image: ghcr.io/myorg/maa-cli:latest
configs:
- configMapName: maa-config
key: config.json
mountPath: /config/config.json
Expose ADB Outside the Cluster
By default each instance has a ClusterIP Service. To expose it externally:
spec:
service:
type: NodePort
nodePort: 30555 # optional — omit to auto-assign
Or via LoadBalancer with cloud annotations:
spec:
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: nlb
Next Steps
- Examples — real-world patterns: MAA automation, wakeInstance, suspendInstance, base-layer init
- API Reference — all spec fields explained (auto-generated)
- kubectl Plugin — full CLI reference
- Architecture — deep-dive into the overlayfs model and controller design