Skip to content

Start here

Installation

Install Holtdroid on Kubernetes with the Helm chart.

  1. Pick a sign-in mode — see Authentication. Every mode except header/none needs a session signing key:

    Terminal window
    kubectl create namespace holtdroid
    kubectl -n holtdroid create secret generic holtdroid \
    --from-literal=SESSION_KEY="$(openssl rand -base64 48)"

    Add whatever else your config references with env: (OIDC client secret, shared password, S3 keys) to the same Secret. External Secrets Operator, Sealed Secrets and friends work just as well — the chart only needs the Secret’s name.

  2. Write your values.

    values.yaml
    publicURL: https://android.example.com
    existingSecret: holtdroid
    config:
    auth:
    mode: anonymous
    sessionKey: env:SESSION_KEY
    providers:
    emulator:
    enabled: true
    nodeSelector: { holtdroid/sessions: "true" } # nodes with binder and a GPU, dedicated to sessions
    slotsPerNode: 4
    # vpn: { locations: [...] } # see the emulator provider page
    ingress:
    enabled: true
    host: android.example.com
  3. Install the chart, from a release:

    Terminal window
    helm install holtdroid oci://ghcr.io/holtdroid/charts/holtdroid -n holtdroid -f values.yaml

    or straight from git, pinning the image to the checked-out commit:

    Terminal window
    git clone https://github.com/holtdroid/holtdroid
    helm install holtdroid holtdroid/deploy/helm/holtdroid -n holtdroid -f values.yaml \
    --set image.tag="$(git -C holtdroid rev-parse HEAD)"
  4. Open the URL and upload an APK with the Upload button, or drop files into the local source directly:

    Terminal window
    kubectl -n holtdroid cp app.apk "$(kubectl -n holtdroid get pod -l app.kubernetes.io/component=api -o name | cut -d/ -f2)":/var/lib/holtdroid/apks/

    For builds that arrive from CI or Slack, point a sync job at an S3 bucket and add it as a source.

Every commit to main publishes ghcr.io/holtdroid/holtdroid:<commit sha>. An Argo CD Application can follow the branch and use the synced commit as the image tag, so new commits roll out on their own:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: holtdroid
namespace: argocd
spec:
project: default
destination:
server: https://kubernetes.default.svc
namespace: holtdroid
source:
repoURL: https://github.com/holtdroid/holtdroid.git
targetRevision: main
path: deploy/helm/holtdroid
helm:
parameters:
- name: image.tag
value: $ARGOCD_APP_REVISION
valuesObject:
publicURL: https://android.example.com
existingSecret: holtdroid
# …the rest of your values
syncPolicy:
automated: { prune: true, selfHeal: true }
syncOptions: [CreateNamespace=true]

Keep it a single-source Application so $ARGOCD_APP_REVISION is the holtdroid commit. Extra manifests (an ExternalSecret, a Traefik Middleware, …) can go into the chart’s extraObjects value. Right after a push the new pod may wait in ImagePullBackOff for a few minutes until that commit’s image is built.

Value Default Meaning
image.repository / tag ghcr.io/holtdroid/holtdroid Tag defaults to the chart’s appVersion
publicURL – External URL; needed for OIDC and secure cookies
existingSecret – Secret exposed as env to every component (for env: references)
config anonymous, filesystem Merged into the api config, see Configuration
api.replicas 1 Safe to raise with a shared sessionKey
api.cacheVolume emptyDir 25Gi Volume for storage.type: filesystem
api.persistence.* RWO PVC, 10Gi Mounted at /var/lib/holtdroid: the default local source and uploads
providers.fake.enabled false Simulated devices
providers.emulator.* disabled Emulator sessions, see Emulator provider
providers.physical.* disabled Phones on USB, see Phone provider
extraProviders [] Out-of-tree providers: [{name, url}]
ingress.* disabled Standard Ingress
telemetry.* off Prometheus metrics and tracing, see Monitoring
audit.* stdout Audit log of the api and ssh-gateway, same page
extraObjects [] Extra manifests, rendered with tpl