Skip to content

add flag to override host/port for the emulators#10761

Open
aalej wants to merge 6 commits into
mainfrom
aalej_feat-em-host-override
Open

add flag to override host/port for the emulators#10761
aalej wants to merge 6 commits into
mainfrom
aalej_feat-em-host-override

Conversation

@aalej

@aalej aalej commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Description

Proposed fix for #10756. Adding a flag for each emulator might make it difficult to maintain since we would have to add a flag whenever a new emulator is added, so opt for using a single flag to override host/port for the emulators. Usage: firebase emulators:start --host-overrides "functions:[::1]:8082,auth:9098".

Scenarios Tested

ipv6 address - works
$ firebase emulators:start --host-overrides "functions:[::1]:8082,auth:9098"
i  emulators: Starting emulators: auth, functions, firestore, extensions
i  emulators: Detected demo project ID "demo-no-project", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.
i  firestore: Firestore Emulator logging to firestore-debug.log
✔  firestore: Firestore Emulator was started in standard edition.
✔  firestore: Firestore Emulator UI websocket is running on 9150.
i  functions: Watching "/Users/PATH/issues/10756/functions" for Cloud Functions...
⚠  functions: Your requested "node" version "24" doesn't match your global version "20". Using node@20 from host.
Serving at port 8163

✔  functions: Loaded functions definitions from source: helloWorld.
✔  functions[us-central1-helloWorld]: http function initialized (http://[::1]:8082/demo-no-project/us-central1/helloWorld).

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://127.0.0.1:4000/               │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬──────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI              │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Authentication │ 127.0.0.1:9098 │ http://127.0.0.1:4000/auth       │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Functions      │ [::1]:8082     │ http://127.0.0.1:4000/functions  │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Firestore      │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore  │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Extensions     │ [::1]:8082     │ http://127.0.0.1:4000/extensions │
└────────────────┴────────────────┴──────────────────────────────────┘
  Emulator Hub host: 127.0.0.1 port: 4400
  Other reserved ports: 4500, 9150
┌─────────────────────────┬───────────────┬─────────────────────┐
│ Extension Instance Name │ Extension Ref │ View in Emulator UI │
└─────────────────────────┴───────────────┴─────────────────────┘
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
 
ipv4 address - works
$ firebase emulators:start --host-overrides functions:127.0.0.1:8082,auth:9098
i  emulators: Starting emulators: auth, functions, firestore, extensions
i  emulators: Detected demo project ID "demo-no-project", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.
i  firestore: Firestore Emulator logging to firestore-debug.log
✔  firestore: Firestore Emulator was started in standard edition.
✔  firestore: Firestore Emulator UI websocket is running on 9150.
i  functions: Watching "/Users/PATH/firebase-tools/issues/10756/functions" for Cloud Functions...
⚠  functions: Your requested "node" version "24" doesn't match your global version "20". Using node@20 from host.
Serving at port 8615

✔  functions: Loaded functions definitions from source: helloWorld.
✔  functions[us-central1-helloWorld]: http function initialized (http://127.0.0.1:8082/demo-no-project/us-central1/helloWorld).

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://127.0.0.1:4000/               │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬──────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI              │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Authentication │ 127.0.0.1:9098 │ http://127.0.0.1:4000/auth       │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Functions      │ 127.0.0.1:8082 │ http://127.0.0.1:4000/functions  │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Firestore      │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore  │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Extensions     │ 127.0.0.1:8082 │ http://127.0.0.1:4000/extensions │
└────────────────┴────────────────┴──────────────────────────────────┘
  Emulator Hub host: 127.0.0.1 port: 4400
  Other reserved ports: 4500, 9150
┌─────────────────────────┬───────────────┬─────────────────────┐
│ Extension Instance Name │ Extension Ref │ View in Emulator UI │
└─────────────────────────┴───────────────┴─────────────────────┘
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
emulator:port - works
$ firebase emulators:start --host-overrides functions:9098
i  emulators: Starting emulators: auth, functions, firestore, extensions
i  emulators: Detected demo project ID "demo-no-project", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.
i  firestore: Firestore Emulator logging to firestore-debug.log
✔  firestore: Firestore Emulator was started in standard edition.
✔  firestore: Firestore Emulator UI websocket is running on 9150.
i  functions: Watching "/Users/PATH/firebase-tools/issues/10756/functions" for Cloud Functions...
⚠  functions: Your requested "node" version "24" doesn't match your global version "20". Using node@20 from host.
Serving at port 8594

✔  functions: Loaded functions definitions from source: helloWorld.
✔  functions[us-central1-helloWorld]: http function initialized (http://127.0.0.1:9098/demo-no-project/us-central1/helloWorld).

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://127.0.0.1:4000/               │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬──────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI              │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Authentication │ 127.0.0.1:9099 │ http://127.0.0.1:4000/auth       │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Functions      │ 127.0.0.1:9098 │ http://127.0.0.1:4000/functions  │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Firestore      │ 127.0.0.1:8080 │ http://127.0.0.1:4000/firestore  │
├────────────────┼────────────────┼──────────────────────────────────┤
│ Extensions     │ 127.0.0.1:9098 │ http://127.0.0.1:4000/extensions │
└────────────────┴────────────────┴──────────────────────────────────┘
  Emulator Hub host: 127.0.0.1 port: 4400
  Other reserved ports: 4500, 9150
┌─────────────────────────┬───────────────┬─────────────────────┐
│ Extension Instance Name │ Extension Ref │ View in Emulator UI │
└─────────────────────────┴───────────────┴─────────────────────┘
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

invalid format - correctly throws error

$ firebase emulators:start --host-overrides functions:

Error: Invalid --host-overrides value "functions:". Emulator overrides must be in the format <emulator>:<port> or <emulator>:<host>:<port>.
$ firebase emulators:start --host-overrides functions

Error: Invalid --host-overrides value "functions". Emulator overrides must be in the format <emulator>:<port> or <emulator>:<host>:<port>.
$ firebase emulators:start --host-overrides functions:not-a-port

Error: Invalid --host-overrides value "functions:not-a-port". Port must be a number, or specify host and port as <emulator>:<host>:<port>.
$ firebase emulators:start --host-overrides invalid-emulator:9098

Error: Invalid --host-overrides value. "invalid-emulator" is not a valid emulator name. Valid emulators are: auth, hub, functions, firestore, database, hosting, apphosting, pubsub, ui, logging, storage, extensions, eventarc, dataconnect, tasks

Sample Commands

firebase emulators:start --host-overrides functions:127.0.0.1:8082,auth:9098

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a --host-overrides flag for the emulators:start and emulators:exec commands, allowing users to dynamically override the host and port of specific emulators. It includes parsing logic for various host/port formats (including IPv6) and corresponding unit tests. The review feedback suggests enhancing the robustness of the parsing logic by adding defensive type checks, trimming whitespace from input tokens to prevent parsing failures, rejecting overrides for the extensions emulator, and validating that parsed ports fall within the valid TCP range (1 to 65535).

Comment thread src/emulator/commandUtils.ts Outdated
Comment on lines +158 to +160
if (options.hostOverrides) {
options.hostOverrides = parseHostOverrides(options.hostOverrides);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add a defensive type check to ensure options.hostOverrides is a string before parsing it. If beforeEmulatorCommand is called programmatically or in tests where options.hostOverrides is already parsed into an object, calling parseHostOverrides on it again will throw a runtime error.

Suggested change
if (options.hostOverrides) {
options.hostOverrides = parseHostOverrides(options.hostOverrides);
}
if (typeof options.hostOverrides === "string") {
options.hostOverrides = parseHostOverrides(options.hostOverrides);
}
References
  1. TypeScript: Use strict null checks and handle undefined/null explicitly. (link)

Comment thread src/emulator/commandUtils.ts Outdated
Comment on lines +274 to +275
const overrides = hostOverrides.split(",");
for (const override of overrides) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Trim each override and filter out empty elements when splitting hostOverrides. This prevents parsing failures if the user includes spaces after commas (e.g., firestore:8080, auth:9099) or trailing commas.

Suggested change
const overrides = hostOverrides.split(",");
for (const override of overrides) {
const overrides = hostOverrides.split(",").map((o) => o.trim()).filter(Boolean);
for (const override of overrides) {

Comment thread src/emulator/commandUtils.ts Outdated
Comment on lines +283 to +290
const emulatorName = override.substring(0, colonIndex);
if (!isEmulator(emulatorName)) {
throw new FirebaseError(
`Invalid --host-overrides value. "${emulatorName}" is not a valid emulator name. Valid emulators are: ${Object.values(
Emulators,
).join(", ")}`,
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Trim the parsed emulatorName to prevent whitespace issues. Additionally, explicitly throw an error if the user tries to override the extensions emulator. Since extensions runs on the same port as functions and is excluded from getListenConfig, any override for extensions would be silently ignored. Directing the user to override functions instead prevents confusion.

    const emulatorName = override.substring(0, colonIndex).trim();
    if (!isEmulator(emulatorName)) {
      throw new FirebaseError(
        `Invalid --host-overrides value. "${emulatorName}" is not a valid emulator name. Valid emulators are: ${Object.values(
          Emulators,
        ).join(", ")}`,
      );
    }
    if (emulatorName === Emulators.EXTENSIONS) {
      throw new FirebaseError(
        `Invalid --host-overrides value. The "extensions" emulator runs on the same port as the "functions" emulator. Please override "functions" instead.`
      );
    }
References
  1. Throw FirebaseError for expected, user-facing errors. (link)

Comment thread src/emulator/commandUtils.ts Outdated
Comment on lines +292 to +293
const hostOverride = override.substring(colonIndex + 1);
if (hostOverride.length === 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Trim hostOverride to ensure that any leading or trailing whitespace around the host/port portion is ignored, preventing regex validation failures.

Suggested change
const hostOverride = override.substring(colonIndex + 1);
if (hostOverride.length === 0) {
const hostOverride = override.substring(colonIndex + 1).trim();
if (hostOverride.length === 0) {

Comment thread src/emulator/commandUtils.ts Outdated
Comment on lines +315 to +316
const [hostString, portString] = hostOverride.split(":");
if (!/^\d+$/.test(portString)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Trim the split host and port strings to prevent validation failures if there are spaces around the colon (e.g., localhost : 8080).

Suggested change
const [hostString, portString] = hostOverride.split(":");
if (!/^\d+$/.test(portString)) {
const [hostString, portString] = hostOverride.split(":").map((s) => s.trim());
if (!/^\d+$/.test(portString)) {

Comment thread src/emulator/commandUtils.ts Outdated
Comment on lines +333 to +337
if (isNaN(port)) {
throw new FirebaseError(
`Invalid --host-overrides value "${override}". Port must be a number.`,
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Validate that the parsed port is within the valid TCP port range (1 to 65535). Currently, ports like 0 or 999999 are accepted during parsing because they consist of digits, but they are not valid TCP ports. Validating the port range early prevents binding failures later on.

Suggested change
if (isNaN(port)) {
throw new FirebaseError(
`Invalid --host-overrides value "${override}". Port must be a number.`,
);
}
if (isNaN(port) || port < 1 || port > 65535) {
throw new FirebaseError(
`Invalid --host-overrides value "${override}". Port must be a valid number between 1 and 65535.`,
);
}
References
  1. Throw FirebaseError for expected, user-facing errors. (link)

@joehan
joehan self-requested a review July 7, 2026 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants