Export and import data
Export from a running emulator
Section titled “Export from a running emulator”Run this in another terminal while fireemu up is running:
npx fireemu emulators:export ./emulator-data--force overwrites a non-empty destination.
Import at startup and export on exit
Section titled “Import at startup and export on exit”--import <dir> loads an export at startup. --export-on-exit without a directory writes the state back to the same directory when the run ends:
npx fireemu exec --import ./emulator-data --export-on-exit -- npm test- An empty
--importdirectory starts with no data, so the first run needs no separate command. A missing directory is refused, so a mistyped path cannot start an empty suite, unless it is also where--export-on-exitwrites. - A directory that holds files but no
firebase-export-metadata.jsonis refused. - Realtime Database imports are validated before any product installs data. Under
strict, a lone surrogate in an imported JSON string value is refused; theemulatorprofile replaces it with?, as the official emulator does. Invalid keys are refused in both profiles. See Realtime Database import. - A failed
--export-on-exitis printed as anerror:line, and the run exits 1 when the command succeeded. The official CLI only warns and exits 0; Fireemu differs so that a CI job that depends on the saved data cannot pass without it. - The export directory may be a volume mount point, such as a container volume mounted at
/data. A mount point cannot be replaced by an atomic rename, so the export is staged inside it and moved into place, the oldfirebase-export-metadata.jsonleaving first and the new one arriving last. That replacement is not atomic: an interrupted export leaves a directory without the manifest, which--importrefuses instead of loading part of it. Any other directory is still replaced atomically.
Where to export
Section titled “Where to export”Before starting a persistent run, inspect the destination without creating directories or changing permissions:
npx fireemu doctor --export-dir "$HOME/.local/share/fireemu/emulator-data"On Unix-like systems, the destination and every existing ancestor are checked for ownership and permissions, including the home directory: each must be owned by you or root and not writable by other users, except a root-owned directory with the sticky bit such as /tmp (on macOS /tmp is a root-owned link to /private/tmp, which is followed).
- Shared directories. In a sticky directory other users can create entries but cannot rename or remove yours, so
--export-on-exit /tmp/emulator-dataworks. Another user can still take the name first, which refuses the export, so a dedicated directory of your own is the more reliable choice on a shared machine. Below/tmp, an export directory that already exists must be yours and not writable by other users (chmod 700). - Refused. A directory other users can write without the sticky bit, or another user’s directory.
- Symlinks. Every symlink on the export path is checked. It is followed only when root owns it, or when you own it in a directory only you (or root) can write, so a link another user makes in
/tmpis never followed...right after a symlink is refused. The directory holding a link must pass the same checks as an ancestor (a volume that enforces ownership, no ACL entry for other users). - When the checks run.
--export-on-exitchecks before the daemon or child command starts and validates again when exporting, since permissions can change. It keeps the export directory’s parent open from the preflight to the end of the run, so nothing renamed or linked on the way meanwhile is followed. - Volume mounts. A volume mount destination needs to be writable even when its parent need not be.
- macOS: an ancestor directory with an ACL is refused when the ACL has an
allowentry for anyone but the owner, root or the user running the export, whatever permissions the entry shows (the kernel grants rights thatls -ledoes not list).denyentries are accepted, so a standard home directory (which carriesgroup:everyone deny delete) and the folders below it work. When an export is refused for an ACL, the message names the entry; remove it (chmod -a) or export to another directory, for example one under$TMPDIR. Check an ancestor with/bin/ls -led <directory>. A destination on a volume that ignores ownership (exFAT and FAT sticks and cards, or an external APFS volume with “Ignore ownership on this volume” turned on) is refused, because the owner and the mode of its directories do not show who can change them; export to a volume that enforces ownership. A volume without atomic directory rename (exFAT, FAT, some network filesystems) cannot publish an export either: use a directory on an APFS volume. - Configuration discovery permits group-writable files for private-group setups, but export publication keeps its stricter cleanup safety rules.
- Windows:
emulators:exportand--export-on-exitare not currently available. Import and the rest of the emulators work.
Export independent test worlds
Section titled “Export independent test worlds”In a test world, hub: true enables a private Hub for the world’s existing CLI export command. Use an absolute caller-owned export path outside world.directory:
import { resolve } from 'node:path';import { createTestWorld } from 'fireemu/testing';
const exportPath = resolve('./emulator-data');const services = ['auth', 'firestore', 'storage'];const world = await createTestWorld({ services, hub: true });try { await world.run('fireemu', [ 'emulators:export', exportPath, '--project', world.projectId, ]);} finally { await world.dispose();}const restored = await createTestWorld({ services, import: exportPath });try { // Connect fresh SDK clients through restored.endpoints or restored.run(...).} finally { await restored.dispose();}Pass --project explicitly. The helper owns the Hub’s OS-assigned port and private locator; same-project worlds stay independent. For a development binary, use its absolute path for both binaryPath and the export command.
Exports preserve Auth users and password credentials, Firestore documents, Storage bytes and metadata, and Realtime Database data. They do not preserve App Check state, virtual clocks, Functions process memory or the validity of previously issued tokens. Imports are snapshotted at creation, so later edits to the original export do not affect reset. Exports outside the world directory remain caller-owned after disposal. See the full export and import guide.