This guide covers how to manage virtual devices (Android emulators and iOS simulators) across all platforms in the Devbox mobile development plugins.
Device management in Devbox plugins follows a declarative approach. You define devices in JSON files, and the plugins handle creating, updating, and managing the actual virtual devices.
Key concepts:
- Device definitions - JSON files describing device configurations
- Lock files - Generated files tracking which devices to evaluate/create
- Device filtering - Control which devices are used for testing or CI
- Min/max convention - Standard naming for minimum and maximum platform versions
Android device definitions specify AVD (Android Virtual Device) configurations. Each JSON file in your devices directory defines one device.
Schema:
{
"name": "pixel_api30",
"api": 30,
"device": "pixel",
"tag": "google_apis",
"preferred_abi": "x86_64"
}Fields:
name(required) - Device identifier, used in commandsapi(required) - Android API level (21-36)device(required) - AVD device profile (pixel, medium_phone, tablet, etc.)tag(optional) - System image taggoogle_apis- Google APIs (recommended)google_apis_playstore- Google APIs + Play Storeplay_store- AOSP + Play Storeaosp_atd- AOSP Automated Test Devicegoogle_atd- Google ATD (fastest boot, minimal apps)
preferred_abi(optional) - CPU architecturex86_64- Intel 64-bit (fastest on x86 hosts)arm64-v8a- ARM 64-bit (required for Apple Silicon Macs)x86- Intel 32-bit (legacy)
Common profiles:
pixel- Pixel phone (5.0", 1080x1920, 420dpi)pixel_xl- Pixel XL (5.5", 1440x2560, 560dpi)pixel_3a- Pixel 3a (5.6", 1080x2220, 440dpi)medium_phone- Generic medium phone (6.3", 1080x2400, 420dpi)tablet- Generic tablet (10.1", 1920x1200, 240dpi)
Example: minimum supported device
{
"name": "pixel_api21",
"api": 21,
"device": "pixel",
"tag": "google_apis"
}Example: maximum/latest device
{
"name": "medium_phone_api36",
"api": 36,
"device": "medium_phone",
"tag": "google_apis"
}iOS device definitions specify simulator configurations. Each JSON file in your devices directory defines one simulator.
Schema:
{
"name": "iPhone 15 Pro",
"runtime": "17.5"
}Fields:
name(required) - iOS device type (e.g., "iPhone 15", "iPad Pro")runtime(required) - iOS version (e.g., "17.5", "18.0", "15.4")
Common device names:
iPhone 13,iPhone 14,iPhone 15,iPhone 16,iPhone 17iPhone 13 Pro,iPhone 14 Pro,iPhone 15 ProiPhone SE (3rd generation)iPad Pro (11-inch),iPad Pro (12.9-inch)iPad Air,iPad mini
Example: minimum supported device
{
"name": "iPhone 13",
"runtime": "15.4"
}Example: maximum/latest device
{
"name": "iPhone 17",
"runtime": "26.2"
}To see all available device types and runtimes on your system:
xcrun simctl list devicetypes
xcrun simctl list runtimesUse min.json and max.json as standard device names for testing minimum and maximum platform versions.
Benefits:
- Clear semantic meaning for version boundaries
- Easy to reference in CI configuration
- Standardized across projects
Example directory structure:
The exact path of the devices directory depends on how the plugin is included in your project (local path vs GitHub reference). Inside your devbox.d folder, you will have a platform directory containing a devices/ subdirectory:
<your-devices-directory>/
├── min.json # Minimum supported version (e.g., API 21 or iOS 15.4)
├── max.json # Latest available version (e.g., API 36 or iOS 26.2)
└── custom.json # Custom device for specific testing
CI usage:
{
"env": {
"ANDROID_DEVICES": "min,max",
"IOS_DEVICES": "min,max",
"ANDROID_DEFAULT_DEVICE": "max",
"IOS_DEFAULT_DEVICE": "max"
}
}View all device definitions:
devbox run android.sh devices listOutput shows:
- Device name
- API level
- Device profile
- System image tag
- Preferred ABI (if specified)
Create a new device definition:
devbox run android.sh devices create <name> --api <level> --device <profile> [options]Examples:
Basic device:
devbox run android.sh devices create pixel_api30 \
--api 30 \
--device pixel \
--tag google_apisWith specific ABI:
devbox run android.sh devices create tablet_api35 \
--api 35 \
--device tablet \
--tag google_apis_playstore \
--abi x86_64High-performance testing device:
devbox run android.sh devices create test_api34 \
--api 34 \
--device medium_phone \
--tag google_atd \
--abi x86_64Modify existing device definitions:
devbox run android.sh devices update <name> [options]Examples:
Change API level:
devbox run android.sh devices update pixel_api30 --api 31Change device profile:
devbox run android.sh devices update pixel_api30 --device pixel_xlRename device:
devbox run android.sh devices update pixel_api30 --name pixel_api31Multiple changes:
devbox run android.sh devices update old_device \
--name new_device \
--api 35 \
--device medium_phone \
--tag google_apisRemove a device definition:
devbox run android.sh devices delete <name>Example:
devbox run android.sh devices delete pixel_api30View a specific device configuration:
devbox run android.sh devices show <name>Example:
devbox run android.sh devices show maxView all device definitions:
devbox run ios.sh devices listOutput shows:
- Device name
- iOS runtime version
Create a new device definition:
devbox run ios.sh devices create <name> --runtime <version>Examples:
iPhone device:
devbox run ios.sh devices create iphone15 --runtime 17.5iPad device:
devbox run ios.sh devices create ipad_pro --runtime 18.0Older device for compatibility testing:
devbox run ios.sh devices create iphone13 --runtime 15.4Modify existing device definitions:
devbox run ios.sh devices update <name> [options]Examples:
Change runtime version:
devbox run ios.sh devices update iphone15 --runtime 18.0Rename device:
devbox run ios.sh devices update iphone15 --name iphone15_proBoth changes:
devbox run ios.sh devices update iphone15 \
--name iphone16 \
--runtime 18.0Remove a device definition:
devbox run ios.sh devices delete <name>Example:
devbox run ios.sh devices delete iphone15View a specific device configuration:
devbox run ios.sh devices show <name>Example:
devbox run ios.sh devices show maxEnsure actual simulators match device definitions:
devbox run ios.sh devices syncThis command:
- Reads the lock file
- Creates missing simulators
- Recreates simulators with mismatched configurations
- Reports matched, recreated, created, and skipped simulators
Lock files optimize CI builds by limiting which SDK versions and system images are evaluated.
Without lock files:
- Nix evaluates all possible device configurations
- Downloads system images for all API levels
- Slower CI builds
With lock files:
- Only evaluates specified devices
- Downloads only required system images
- Faster CI builds
Location: devices.lock inside your Android devices directory
Format:
{
"devices": [
{
"name": "medium_phone_api36",
"api": 36,
"device": "medium_phone",
"tag": "google_apis"
},
{
"name": "pixel_api21",
"api": 21,
"device": "pixel",
"tag": "google_apis"
}
],
"checksum": "2f3ab0e3cefd3e9909185c0717dc9d63038da1e81625eb6fce585e3af446bfef"
}Fields:
devices- Array of device configurations to evaluatechecksum- SHA-256 hash of all device definition files
Location: devices.lock inside your iOS devices directory
Format:
{
"devices": [
{
"name": "iPhone 17",
"runtime": "26.2"
},
{
"name": "iPhone 13",
"runtime": "15.4"
}
],
"checksum": "dd575d31a5adf2f471655389df301215f6ef7130ca284d433929b08b68e42890"
}Always regenerate lock files after creating, updating, or deleting devices.
Android:
devbox run android.sh devices evaliOS:
devbox run ios.sh devices evalLock files should be committed to version control.
The plugins automatically validate lock file checksums against device definitions.
If checksums mismatch:
[WARN] Lock file checksum mismatch
Expected: abc123...
Got: def456...
Run: devbox run android.sh devices eval
This is a warning, not an error. Execution continues, but you should regenerate the lock file.
Control which devices are evaluated using environment variables.
Evaluate all devices (default):
{
"env": {
"ANDROID_DEVICES": ""
}
}Evaluate specific devices:
{
"env": {
"ANDROID_DEVICES": "min,max"
}
}Evaluate single device:
{
"env": {
"ANDROID_DEVICES": "max"
}
}Evaluate all devices (default):
{
"env": {
"IOS_DEVICES": ""
}
}Evaluate specific devices:
{
"env": {
"IOS_DEVICES": "min,max"
}
}Evaluate single device:
{
"env": {
"IOS_DEVICES": "max"
}
}Development (evaluate all):
{
"env": {
"ANDROID_DEVICES": "",
"IOS_DEVICES": ""
}
}CI (min/max only):
{
"env": {
"ANDROID_DEVICES": "min,max",
"IOS_DEVICES": "min,max"
}
}Feature testing (specific device):
{
"env": {
"ANDROID_DEVICES": "tablet_api35",
"IOS_DEVICES": "ipad_pro"
}
}After changing device filtering, regenerate lock files:
devbox run android.sh devices eval
devbox run ios.sh devices evalTest compatibility on minimum and maximum supported versions.
Setup:
{
"env": {
"ANDROID_DEVICES": "min,max",
"IOS_DEVICES": "min,max"
}
}Android test script:
#!/bin/bash
set -euo pipefail
for device in min max; do
echo "Testing on $device"
devbox run android.sh emulator start "$device"
# Run your project-specific test command here
# e.g., devbox run ./gradlew connectedAndroidTest
devbox run android.sh emulator stop
doneiOS test script:
#!/bin/bash
set -euo pipefail
for device in min max; do
echo "Testing on $device"
devbox run ios.sh simulator start "$device"
# Run your project-specific test command here
# e.g., devbox run xcodebuild test -scheme MyApp
devbox run ios.sh simulator stop
doneRun tests on multiple devices simultaneously using process-compose.
process-compose.yaml:
version: "0.5"
processes:
test-android-min:
command: |
devbox run android.sh emulator start min
# Run your project-specific test command here
depends_on:
setup:
condition: process_completed
test-android-max:
command: |
devbox run android.sh emulator start max
# Run your project-specific test command here
depends_on:
setup:
condition: process_completed
test-ios-min:
command: |
devbox run ios.sh simulator start min
# Run your project-specific test command here
depends_on:
setup:
condition: process_completed
test-ios-max:
command: |
devbox run ios.sh simulator start max
# Run your project-specific test command here
depends_on:
setup:
condition: process_completed
cleanup:
command: |
devbox run android.sh emulator stop
devbox run ios.sh simulator stop
depends_on:
test-android-min:
condition: process_completed
test-android-max:
condition: process_completed
test-ios-min:
condition: process_completed
test-ios-max:
condition: process_completedRun parallel tests:
devbox run process-compose upOverride device selection for individual test runs.
Android:
# Start emulator with default device
devbox run android.sh emulator start
# Start emulator with specific device
devbox run android.sh emulator start pixel_api28iOS:
# Start simulator with default device
devbox run ios.sh simulator start
# Start simulator with specific device
devbox run ios.sh simulator start iphone15Build, test, and deploy commands are project-specific and must be defined by you in your devbox.json scripts section.
Test all combinations of devices and configurations.
Matrix configuration:
#!/bin/bash
set -euo pipefail
ANDROID_DEVICES=("min" "max" "tablet_api35")
IOS_DEVICES=("min" "max" "ipad_pro")
CONFIGS=("debug" "release")
for device in "${ANDROID_DEVICES[@]}"; do
for config in "${CONFIGS[@]}"; do
echo "Testing Android $device with $config"
devbox run android.sh emulator start "$device"
# Run your project-specific test command here, e.g.:
# devbox run ./gradlew connectedAndroidTest -PbuildType="$config"
devbox run android.sh emulator stop
done
done
for device in "${IOS_DEVICES[@]}"; do
for config in "${CONFIGS[@]}"; do
echo "Testing iOS $device with $config"
devbox run ios.sh simulator start "$device"
# Run your project-specific test command here, e.g.:
# devbox run xcodebuild test -scheme MyApp -configuration "$config"
devbox run ios.sh simulator stop
done
doneSpecify a default device used when no device is explicitly provided.
Android:
{
"env": {
"ANDROID_DEFAULT_DEVICE": "max"
}
}iOS:
{
"env": {
"IOS_DEFAULT_DEVICE": "max"
}
}Usage with plugin-provided commands:
# Start emulator/simulator with default device
devbox run android.sh emulator start
devbox run ios.sh simulator start
# Start emulator/simulator with specific device
devbox run android.sh emulator start min
devbox run ios.sh simulator start minFor CI environments, use min/max filtering for fast, comprehensive testing.
devbox.json:
{
"env": {
"ANDROID_DEVICES": "min,max",
"IOS_DEVICES": "min,max",
"ANDROID_DEFAULT_DEVICE": "max",
"IOS_DEFAULT_DEVICE": "max"
}
}.github/workflows/ci.yml:
jobs:
test-android:
runs-on: ubuntu-latest
strategy:
matrix:
device: [min, max]
steps:
- uses: actions/checkout@v3
- run: devbox install
- run: devbox run android.sh emulator start ${{ matrix.device }}
- run: devbox run your-android-test-script # Define in devbox.json scripts
- run: devbox run android.sh emulator stop
test-ios:
runs-on: macos-latest
strategy:
matrix:
device: [min, max]
steps:
- uses: actions/checkout@v3
- run: devbox install
- run: devbox run ios.sh simulator start ${{ matrix.device }}
- run: devbox run your-ios-test-script # Define in devbox.json scripts
- run: devbox run ios.sh simulator stopFor development, use all devices or latest device for faster iteration.
All devices (comprehensive testing):
{
"env": {
"ANDROID_DEVICES": "",
"IOS_DEVICES": "",
"ANDROID_DEFAULT_DEVICE": "max",
"IOS_DEFAULT_DEVICE": "max"
}
}Latest device only (fast iteration):
{
"env": {
"ANDROID_DEVICES": "max",
"IOS_DEVICES": "max",
"ANDROID_DEFAULT_DEVICE": "max",
"IOS_DEFAULT_DEVICE": "max"
}
}For feature development targeting specific devices or form factors.
Tablet testing:
{
"env": {
"ANDROID_DEVICES": "tablet_api35",
"IOS_DEVICES": "ipad_pro",
"ANDROID_DEFAULT_DEVICE": "tablet_api35",
"IOS_DEFAULT_DEVICE": "ipad_pro"
}
}Older device compatibility:
{
"env": {
"ANDROID_DEVICES": "min",
"IOS_DEVICES": "min",
"ANDROID_DEFAULT_DEVICE": "min",
"IOS_DEFAULT_DEVICE": "min"
}
}Use clear, descriptive names for devices.
Good names:
min.json,max.json- Semantic version boundariespixel_api30.json- Descriptive with API leveliphone15_pro.json- Specific device modeltablet_api35.json- Form factor and API level
Avoid:
device1.json,device2.json- No contexttest.json,temp.json- Unclear purposed30.json,i15.json- Cryptic abbreviations
Commit device definitions and lock files.
What to commit:
*.jsonfiles in your devices directories - Device definitionsdevices.lockfiles in your devices directories - Lock files
What to ignore:
.devbox/virtenv/- Generated runtime filesreports/- Test outputsDerivedData/- Build artifacts
.gitignore:
.devbox/virtenv/
reports/
DerivedData/
*.log
Regenerate lock files after any device changes.
Workflow:
- Modify device definitions (create/update/delete)
- Regenerate lock file:
devbox run {platform}.sh devices eval - Commit both device definitions and lock files
- CI uses lock files for fast, deterministic builds
Example:
# Create new device
devbox run android.sh devices create pixel_api35 \
--api 35 \
--device pixel \
--tag google_apis
# Regenerate lock file
devbox run android.sh devices eval
# Commit both the device definition and updated lock file
git add <your-android-devices-directory>/
git commit -m "feat(android): add pixel_api35 device"Keep device definitions aligned with project requirements.
Regular maintenance:
- Update max devices when new OS versions release
- Remove obsolete device configurations
- Keep min devices aligned with minimum supported versions
- Test min/max periodically to catch compatibility issues
Example: updating to new Android API:
# Update max device
devbox run android.sh devices update max --api 36
# Regenerate lock
devbox run android.sh devices eval
# Test compatibility by starting the emulator with the updated device
devbox run android.sh emulator start maxFor CI:
- Use
min,maxdevice filtering - Commit lock files (avoid regenerating on every build)
- Use
google_atdtag for faster Android emulator boots - Enable
IOS_DOWNLOAD_RUNTIME=0if runtimes are pre-installed
For development:
- Evaluate only devices you're actively testing
- Use default device for quick iterations
- Evaluate all devices before committing
Example CI optimization:
{
"env": {
"ANDROID_DEVICES": "min,max",
"IOS_DEVICES": "min,max",
"ANDROID_DEFAULT_DEVICE": "max",
"IOS_DEFAULT_DEVICE": "max",
"IOS_DOWNLOAD_RUNTIME": "0"
}
}Device commands include validation warnings but never block execution.
Lock file mismatch (warning):
[WARN] Lock file checksum mismatch
Run: devbox run android.sh devices eval
Execution continues. Regenerate lock file when convenient.
Missing device definition (error):
[ERROR] Device 'pixel_api30' not found in devices directory
Available devices: min, max
Execution stops. Create device or use an available device name.
Missing runtime (warning):
[WARN] Runtime iOS 17.5 not available
Run: xcodebuild -downloadPlatform iOS
Or set IOS_DOWNLOAD_RUNTIME=1 for automatic downloads
Execution continues if auto-download is enabled. Otherwise, manually download runtime.
For React Native or hybrid apps, manage both Android and iOS devices.
Setup:
{
"include": ["github:segment-integrations/devbox-plugins?dir=plugins/react-native"],
"env": {
"ANDROID_DEVICES": "min,max",
"IOS_DEVICES": "min,max",
"ANDROID_DEFAULT_DEVICE": "max",
"IOS_DEFAULT_DEVICE": "max"
}
}Directory structure:
The exact layout of your devbox.d directory depends on how the plugin is included (local path vs GitHub reference). Each platform will have its own devices directory containing your device definitions and lock file:
<android-devices-directory>/
├── min.json
├── max.json
└── devices.lock
<ios-devices-directory>/
├── min.json
├── max.json
└── devices.lock
Testing workflow:
# Android testing
devbox run android.sh devices list
devbox run android.sh devices eval
devbox run android.sh emulator start min
devbox run android.sh emulator start max
# iOS testing
devbox run ios.sh devices list
devbox run ios.sh devices eval
devbox run ios.sh simulator start min
devbox run ios.sh simulator start maxSymptom: "Device 'device-name' not found"
Solution:
# List available devices
devbox run {platform}.sh devices list
# Check file exists in your devices directory
ls <your-devices-directory>/
# Ensure filename matches (case-sensitive)
# device-name.json should match exactlySymptom: "Lock file checksum mismatch" warning
Solution:
# Regenerate lock file
devbox run android.sh devices eval
devbox run ios.sh devices eval
# Commit updated lock files from your devices directories
git commit -am "chore: update device lock files"Symptom: "System image not available for API X"
Solution:
# Check available system images
sdkmanager --list
# Verify tag is valid for API level
# Some tags (like google_apis_playstore) are not available for all API levels
# Update device to use compatible tag
devbox run android.sh devices update device-name --tag google_apis
devbox run android.sh devices evalSymptom: "Runtime iOS X.X not found"
Solution:
# List available runtimes
xcrun simctl list runtimes
# Download runtime manually
xcodebuild -downloadPlatform iOS
# Or enable auto-download
# In devbox.json:
{
"env": {
"IOS_DOWNLOAD_RUNTIME": "1"
}
}Symptom: iOS simulator creation fails with "already exists"
Solution:
# Sync simulators to match definitions
devbox run ios.sh devices sync
# Or manually delete duplicate simulator
xcrun simctl delete <udid>
# Then recreate from definition
devbox run ios.sh devices syncSymptom: Lock file includes devices not in filter list
Solution:
# Verify environment variable is set
devbox run {platform}.sh config show
# Set in devbox.json, not shell environment
{
"env": {
"ANDROID_DEVICES": "min,max",
"IOS_DEVICES": "min,max"
}
}
# Regenerate lock file after changing filter
devbox run android.sh devices eval
devbox run ios.sh devices eval- Android Guide - Platform-specific Android development
- iOS Guide - Platform-specific iOS development
- React Native Guide - Cross-platform React Native development
- Testing Guide - Comprehensive testing strategies
- Troubleshooting Guide - Common issues and solutions
- Android Reference - Complete Android API reference
- iOS Reference - Complete iOS API reference