-
Notifications
You must be signed in to change notification settings - Fork 1
Emulator
Three-stage rollout:
| Version | What ships |
|---|---|
| v0.19.0 | Scaffolding — /emulator diagnostics (KVM / SDK / AVD / running devices) + ADB install + manual launch guide. Read-only. |
| v0.24.0 | AVD lifecycle on the slim image — + create default, ▶ headless start, ■ stop POST forms. :full emulator-entrypoint.sh written but image not yet published. |
| v0.25.0 |
:full image published (siamakerlab/vibe-coder-server:full, ~3-4 GB) with qemu + Xvfb + x11vnc + websockify + noVNC pre-installed. docker/compose.full.yml example. |
Quick path to a working browser-based emulator (v0.25.0+) see the "compose.full.yml override" section below.
View at /emulator (left nav: "Emulator").
The slim base image (~600 MB) ships JDK + Node + git + the server body — that's it. A full Android emulator needs:
-
KVM (
/dev/kvmaccessible inside the container) — host-specific - qemu-system-x86_64 + libs — ~400 MB
- X server (Xvfb) + websockify + noVNC — ~150 MB
- Android system-image — 1–2 GB depending on API level
Adding all of those to the default image triples its size for users who
will never touch the emulator. The decision: keep the default slim,
provide a :full variant for users who really want in-browser emulator
mirroring, and let :default users still run an emulator on the host
and use the diagnostics page to inspect it.
EmulatorService.diagnose() runs at every page load and reports:
| Item | Source |
|---|---|
| KVM available |
/dev/kvm exists and is readable+writable |
| emulator binary | $ANDROID_HOME/emulator/emulator |
| adb binary | $ANDROID_HOME/platform-tools/adb |
| Installed AVDs | avdmanager list avd -c |
| Running devices |
adb devices (drops the header) |
| Recommendation | dynamic text — what to do next based on the above |
By default the compose service runs unprivileged with no device access. KVM requires you to grant access:
services:
vibe-coder-server:
devices:
- /dev/kvm:/dev/kvm
# If running on a host where /dev/kvm is owned by the kvm group:
group_add:
- "${KVM_GID:-104}" # adjust to match `getent group kvm | cut -d: -f3`And ensure the host operator is in the kvm group:
sudo usermod -aG kvm $USER
# logout / login to take effectWithout KVM the emulator falls back to software emulation — usually 10× slower. Useful for one-off "did the layout render?" checks but not for realistic perf testing.
The /emulator page renders the full guide inline. Excerpts:
docker exec -it --user vibe vibe-coder-server bash
$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager 'system-images;android-35;google_apis;x86_64'
$ANDROID_HOME/cmdline-tools/latest/bin/avdmanager create avd \
-n test \
-k 'system-images;android-35;google_apis;x86_64'
exitdocker exec -d --user vibe vibe-coder-server \
bash -c '$ANDROID_HOME/emulator/emulator -avd test -no-window -no-audio &'-no-window runs without a GUI — fine because v0.19.0 doesn't include
the noVNC display anyway. ADB still works.
Refresh /emulator — the new device should appear under "Running devices".
To install a freshly built APK, ask Claude in the project console:
"Install the latest debug APK on the running emulator and launch the main activity."
Claude knows how to find the APK under
<workspace>/.vibecoder/<projectId>/artifacts/debug/<buildId>/*.apk and
adb install -r it. The internal EmulatorService.installApk(deviceSerial, apkPath)
is also available for direct programmatic use.
The /emulator page exposes three CSRF-protected forms:
| Action | Endpoint | What it runs |
|---|---|---|
| Create default AVD | POST /emulator/avd/create-default |
avdmanager create avd -n vibe-default -k 'system-images;android-35;google_apis;x86_64' -d pixel_6 --force (interactive prompt auto-answered "no") |
| Headless launch |
POST /emulator/avd/launch (form name) |
emulator -avd <name> -no-window -no-audio -no-boot-anim detached |
| Stop a running emulator |
POST /emulator/avd/stop (form serial) |
adb -s <serial> emu kill |
All three are audited (emulator.avd.create / launch / stop). On the
slim image these still work but software emulation makes the boot a
multi-minute affair without KVM.
docker/compose.full.yml swaps the slim image for :full and adds the
host bridges needed for in-container Android emulation + in-browser
mirroring:
services:
vibe-coder-server:
image: ${VIBECODER_IMAGE_FULL:-siamakerlab/vibe-coder-server:full}
devices:
- /dev/kvm:/dev/kvm
group_add:
- "${KVM_GID:-104}" # match `getent group kvm | cut -d: -f3`
ports:
- "${VIBE_PORT:-17880}:17880"
- "${VIBE_NOVNC_PORT:-6080}:6080".env:
VIBECODER_IMAGE_FULL=siamakerlab/vibe-coder-server:full
KVM_GID=104
VIBE_NOVNC_PORT=6080Use it as an override alongside the slim compose:
curl -fsSL https://raw.githubusercontent.com/siamakerlab/vibe-coder-server/main/docker/compose.full.yml -o compose.full.yml
docker compose -f compose.yml -f compose.full.yml up -d --force-recreate vibe-coder-serverAfter bootup:
-
/emulatorshows "KVM (/dev/kvm) ✓ 사용 가능". - Click + 디폴트 AVD 생성 (requires the system-image to have been
downloaded first via
/env-setuporvibe-doctor android). - Click ▶ headless 시작.
- Open
http://<host>:6080/vnc.html→ Connect → emulator screen.
Since v0.42.0 the server proxies noVNC through the same
vibe_session admin authentication. The /emulator page embeds the
noVNC client in an iframe pointed at /emulator/vnc/vnc.html (HTTP
proxy) and /emulator/vnc/websockify (WebSocket proxy) — no host-side
port 6080 exposure required.
How it works:
-
GET /emulator/vnc/{path...}forwards tohttp://127.0.0.1:6080/{path}inside the container. Static HTML / JS / CSS / images load same-origin. -
WS /emulator/vnc/websockifyopens a backend WebSocket tows://127.0.0.1:6080/websockifywith subprotocolbinaryand shuttles binary/text frames in both directions. - Both endpoints are admin-only (
requireAdminOrRedirecton HTTP,device.userId → user.isAdminon the WS handshake). Member and viewer sessions can't reach noVNC. - JDK 11+
java.net.http.HttpClient+WebSocketonly. Zero new dependencies.
In compose.full.yml the ports: ["6080:6080"] block becomes
optional. With the proxy, leaving 6080 inside the container is
enough — and arguably safer.
If you'd rather skip the proxy (e.g. simpler reverse-proxy setup), the original direct exposure still works:
ssh -L 6080:localhost:6080 user@vibe-host
# browser: http://localhost:6080/vnc.htmlBut the proxy path is recommended on multi-user installations because of the role guard.
- ✅
/emulator/vnc/reverse proxy — landed in v0.42.0. - ADB log streaming on the project console for the running emulator.
- Multi-AVD parallel sessions (one per project).
Track the work in CHANGELOG ### Phase 4 (3/n+).
-
Host emulator — install Android Studio on the host, run an AVD
there, ADB-connect to the container if needed
(
adb connect host.docker.internal:5555after enabling ADB-over-TCP). -
Physical device — uncomment the
devices: - /dev/bus/usb:/dev/bus/usbblock incompose.ymlandadb deviceswill pick it up. - Cloud emulator (Firebase Test Lab, AWS Device Farm, Genymotion Cloud) — out of scope for vibe-coder; the operator runs builds + uploads manually for now.