Run in Docker
Skip the local toolchain install — the repo's Docker setup bundles Node 24, Appium 3, JDK, the Android SDK command-line tools, and the UiAutomator2 driver into one image. Useful for reproducible CI and zero-setup onboarding.
Run in Docker (optional)
Skip the local toolchain install — the repo's Docker setup bundles Node 24, Appium 3, JDK 21, the Android SDK command-line tools, and the UiAutomator2 driver into one image. Useful for CI (reproducible runs against a cloud device) and onboarding (contributors who don't want to install Appium / Android Studio locally).
The repo ships a Dockerfile and a docker-compose.yml with three profiles. Pick the one that matches what you're doing.
Build the image (one-time)
All three profile services share one image (taqwright:local). Build it once — subsequent up / run commands reuse the cache.
docker compose build appiumappium?
All three services in docker-compose.yml are profile-gated, so a bare docker compose build sees no services and prints No services to build. Naming any service builds the shared image. Equivalent:
docker compose --profile local-android build.
First-time build: 3–6 minutes on Apple Silicon (Rosetta-emulating the amd64 SDK), 2–4 minutes on Linux/Intel. Final image: ~1.3 GB.
What's in the image
Source of truth is the Dockerfile at the repo root. At a glance, the build layers are:
| Layer | What & why | ~Size |
|---|---|---|
node:24-bookworm |
Debian-based Node 24 LTS — matches engines.node in package.json. |
~250 MB |
| System tools | openjdk-17-jre-headless (Appium's UiAutomator2 driver needs 11+; 17 is what Debian bookworm ships by default) plus Debian's adb package (built for both amd64 and arm64 — Google's own platform-tools is x86_64-only on Linux, so we use Debian's so the image runs natively on Apple Silicon). Plus curl + unzip + ca-certificates for the SDK download. |
~210 MB |
| Android cmdline-tools | Downloaded from Google's CDN at build time. Gives you sdkmanager (used at build time to accept SDK licenses). Java-based, so arch-neutral. We skip installing the platform-tools / system-images / emulator via sdkmanager — Debian's adb covers our adb need, and emulators don't work in Docker Desktop anyway. |
~150 MB |
| Appium 3 + UiAutomator2 driver | npm install -g appium@^3 then appium driver install uiautomator2. No XCUITest — iOS goes through cloud providers. |
~200 MB |
Env baked in (no shell setup needed inside the container):
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-{amd64,arm64} # arch-suffix set by buildx
ANDROID_HOME=/opt/android-sdk
ANDROID_SDK_ROOT=/opt/android-sdk
PATH=$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$PATHDefault command: appium --address 0.0.0.0 --port 4723 --base-path /. The cloud-profile services override this with npx taqwright test --project=….
Dockerfile itself changes, engines.node moves in package.json, or the pinned Appium / UiAutomator2 versions change. Other code changes don't require a rebuild — the test sources mount in via volumes: - .:/work.
Local dev — Android emulator on the host
Appium runs in the container; the emulator runs on your host. The container's adb connects to the emulator over TCP (port 5555). Works on macOS, Windows, and Linux Docker Desktop. Setup is five steps — three on the host, two inside the container.
/dev/kvm on Linux). Docker Desktop on Mac / Windows runs Docker itself inside a VM — nested virtualization isn't passed through, so an emulator in the container would either refuse to boot or crawl. The container is the Appium runtime; the device is always external.
Prerequisite: at least one AVD created in Android Studio (or via avdmanager). Check what you have:
emulator -list-avds
# Pixel_7_API_34 ← if you see names, you're set
# # if empty: open Android Studio → Device Manager → Create DeviceHost-side setup (per emulator session)
# 1. Boot the emulator in the background:
emulator -avd Pixel_7_API_34 &
# 2. Wait for Android to finish booting (~30s typically):
adb wait-for-device
adb shell getprop sys.boot_completed # prints "1" when ready
# 3. Confirm host sees it:
adb devices
# List of devices attached
# emulator-5554 device
# 4. Put it into TCP-listen mode so the container can reach it on :5555.
# NOTE: this gets reset every emulator reboot — re-run after each boot.
adb tcpip 5555
# restarting in TCP mode port: 5555Container-side setup
What the compose file wires up for you (no manual config needed):
- Port 4723 mapped to your host — so
localhost:4723reaches Appium inside the container. ~/.android(read-only volume) — shares your host's adb keypair (adbkey/adbkey.pub) with the container. Emulators you've already authorized on the host accept the container's adb without prompting again. Without this,adb connectwould fail withfailed to authenticate.host.docker.internal:host-gateway— makes the hostname resolve to your host's IP from inside the container (built-in on Docker Desktop, explicit on Linux).
# 5. Start the Appium container (detached so you can exec into it):
docker compose --profile local-android up -d appium
# 6. From inside the container, connect adb to the host emulator:
docker compose exec appium adb connect host.docker.internal:5555
# connected to host.docker.internal:5555
# 7. Verify the container now sees the emulator:
docker compose exec appium adb devices
# List of devices attached
# host.docker.internal:5555 device ← anything other than "device"
# means try again from step 4The chain is live: host emulator ← TCP :5555 ← container's adb ← Appium → port :4723 → your host. Verify Appium is reachable:
curl -s http://localhost:4723/status
# {"value":{"ready":true,"message":"The server is ready...","build":{"version":"3.x.x"}}}From here, you can:
- Run codegen against the containerized Appium:
cdinto your taqwright project, runnpx taqwright codegen. The inspector opens in your browser, defaults tolocalhost:4723— just enter your app'sappium:appPackage+appium:appActivity(pre-install the APK viaadb installon the host first). - Run tests from your host shell:
npx taqwright testagainst a project whoseuse.appiumpoints atlocalhost:4723.
offline, empty, or step 6 says failed to authenticate:
failed to authenticate→ the container's adb has a different keypair than your host. The compose file mounts~/.androidfrom your host into the container (read-only) so the keys match — make sure that volume is present. If you're running an older compose that doesn't have it, copy the keys manually:docker compose cp ~/.android/adbkey appium:/root/.android/adbkey && docker compose cp ~/.android/adbkey.pub appium:/root/.android/adbkey.pub && docker compose exec appium adb kill-server, then retry step 6.- Emulator went to sleep →
adb disconnect host.docker.internal:5555in the container, then re-run step 6. - Emulator was rebooted → re-run step 4 (
adb tcpip 5555) on the host, then step 6. - Host shows no device in step 3 → no emulator running. Boot one via step 1.
- The emulator may show "Allow USB debugging from this computer?" — click Always allow.
When you're done, kill the emulator: adb emu kill (or close its window). And tear down the container: docker compose --profile local-android down.
CI — cloud Android (BrowserStack / LambdaTest)
No local Appium and no local emulator. The test runner container talks directly to the cloud provider. Define an android-cloud project in your taqwright.config.ts with device.provider: 'browserstack' (or 'lambdatest') and your credentials in env.
export BROWSERSTACK_USERNAME=...
export BROWSERSTACK_ACCESS_KEY=...
docker compose --profile ci-cloud-android run --rm tests-cloud-androidThe container mounts your repo at /work, forwards your credentials, and runs npx taqwright test --project=android-cloud. Results land in ./playwright-report/ on the host.
CI — cloud iOS
Same image, same env vars; just a different project name. Your ios-cloud project in taqwright.config.ts needs platform: Platform.IOS and device.provider: 'browserstack' (or 'lambdatest') — the cloud handles all the iOS-specific machinery.
export BROWSERSTACK_USERNAME=...
export BROWSERSTACK_ACCESS_KEY=...
docker compose --profile ci-cloud-ios run --rm tests-cloud-iosVerify the image
Run the doctor inside the container — it'll confirm Node / Appium / adb / java are all present:
docker compose run --rm tests-cloud-android npx taqwright doctorExpected output — the macOS-only lines (xcrun, Xcode, ffmpeg, iOS simulator) are absent because the container is Linux, and only the uiautomator2 Appium driver is in the image (iOS goes through the cloud):
taqwright doctor (v0.0.1)
[ok] Node.js >= 24 — v24.x.x
[ok] adb (Android SDK) — on PATH
[ok] ANDROID_HOME (Appium adb lookup) — ANDROID_HOME=/opt/android-sdk
[ok] java (JDK for UiAutomator2) — on PATH
[ok] JAVA_HOME (UiAutomator2 JDK) — JAVA_HOME=/usr/lib/jvm/java-17-openjdk-arm64
[ok] Appium (test server) — on PATH (v3.x.x)
[ok] Appium drivers — uiautomator2; not installed: xcuitest (iOS)