Run dozens of Expo dev servers on one machine
jetplane is a Metro plugin and a lightweight dev server for Expo & React Native. Every same-dep project shares one transform cache, so each environment costs about 40 MB — not Metro’s ~325 MB idle / ~2 GB cold.
Just want the cross-project cache? Run jetplane init and keep using expo start.
Benchmark · warm path
Memory and warm starts, measured honestly
These are warm-path numbers. jetplane isn’t racing Metro on a cold build — it moves the heavy build to pre-warm time, then serves the pre-built bundle cheaply and shares one transform cache across projects. It complements Metro rather than competing with it: the steady state gets cheap, and the cold-bundle spike that scales linearly across servers goes away.
jetplane in colour; Metro and the web dev servers muted for context. jetplane’s bar is a warm serve of a pre-built bundle — the heavy build ran once at pre-warm time — so this is a footprint comparison, not a like-for-like cold build against Metro. Linear scale — Metro’s cold bundle (~2,018 MB) runs off the axis (torn end) so the single-process servers stay comparable. Resident memory of the whole dev-server process tree (psRSS), Apple Silicon · macOS 15 · bench/RESULTS.md
How it works
A dev runtime built on the vendor / app split
Validated end-to-end on a real device in Expo Go.
node_modules is ~98.5% dead weight
For a real Expo app only ~8 MB of 539 MB is reachable into the bundle. jetplane splits the immutable vendor layer from the tiny mutable app layer and caches the vendor once.
Cross-project transform cache
Transforms are content-addressed by source bytes (root-independent), so the same module transforms once and is reused across different projects — which Metro’s own root-dependent cache cannot do.
Thin, no-Metro dev server
A pre-built bundle is served from a thin process that mmaps it (shared physical pages). No per-project Metro means ~40 MB per environment instead of ~325 MB idle / ~2 GB cold.
Live HMR, reconstructed
Fast Refresh works on the thin server: the /hot WebSocket speaks Metro’s protocol and updates are rebuilt from the bundle + the hot transform — validated on device in Expo Go.
Shared transform service
babel’s ~150 MB resident cost is paid once by a shared service that many thin servers call — so per-project memory stays flat and babel is amortized across the whole fleet.
Built for fleets
~40–55 MB × N + one shared service, versus Metro’s ~325 MB × N idle and ~2 GB × N cold-bundle spikes. A burst of cold bundles is what OOMs a fleet node — jetplane removes it.
Compatibility
Is it a drop-in replacement?
Not wholesale — and it doesn’t need to be. The cache plugin drops into your existing expo start with one line; the experimental thin serve replaces the dev-server role at ~40 MB. It augments Metro; it doesn’t replace the Expo CLI.
| Capability | Metro · expo start | jetplane cache plugin | jetplane thin serve |
|---|---|---|---|
| Drop-in with the Expo CLI (expo start) | it is Metro | +1 line in metro.config.js | separate serve command |
| Runs in Expo Go | |||
| Cross-project transform cache | Metro's keys are root-dependent | ||
| Cold-bundle ~2 GB spike | yes | avoided after 1st build | none |
| Per dev-server memory | ~325 MB idle · ~2 GB cold | ~325 MB (rides Metro) | ~40 MB |
| HMR / Fast Refresh | app-layer | ||
| Full on-demand bundling (any entry, symbolication) | pre-built bundle + app-layer HMR | ||
| Replaces Metro | — | augments Metro | serve role only |
| Setup | none | 1 line | build step + serve |