> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Backstage Configuration

> How to make Backstage dev server reachable externally by replacing localhost bindings, updating URLs, CORS, and listen interface

In this guide you'll find a focused workflow to resolve a common Backstage development issue: the frontend and backend are configured to use `localhost`, which prevents accessing Backstage from an external browser using the server's IP or DNS. This article preserves the original configuration examples and shows the minimal changes required to make Backstage reachable externally while keeping local overrides manageable.

What you'll learn

* Why `localhost` bindings block external access
* Which config keys to update (`app.baseUrl`, `backend.baseUrl`, `backend.listen`, `backend.cors.origin`)
* How to apply changes safely with `app-config.local.yaml`
* How to restart and verify the dev server

Problem overview

* The frontend is configured with `app.baseUrl: http://localhost:3000`. Generated URLs and the webpack dev server advertise `localhost`, so requests sent to the server's external IP are rejected by the host checks or by the server binding.
* The backend uses `localhost` for `baseUrl` and may bind to the loopback interface, or its CORS config may not allow the external frontend origin, causing the browser to block API calls.
* Solution: switch the frontend `app.baseUrl` and backend `baseUrl` to the server IP or DNS name, ensure the backend listens on an interface that accepts external requests (commonly `0.0.0.0`), and add the frontend origin to `backend.cors.origin`.

Example shortened console output showing the dev server running locally:

```bash theme={null}
# Start dev server
> yarn dev
[app]: Loaded config from app-config.yaml
[app]: NOTE: Did not compute git version or commit hash, could not execute the git command line utility
[app]: [webpack-dev-server] Project is running at: http://localhost:3000/
[backend]: Loading config from MergedConfigSource{FileConfigSource{path="/root/demo/backstage/app-config.yaml"}, FileConfigSource{path="/root/demo/backstage/app-config.local.yaml"}, EnvConfigSource{count=0}}
[backend]: 2025-02-13T00:04:45.134Z rootHttpRouter info Listening on :7007
[backend]: 2025-02-13T00:04:45.136Z backstage info Plugin initialization started
```

You may see the browser show a connection refused error when navigating to the server IP on port 3000 (because `app.baseUrl` is `localhost`):

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/cTFshlV6LNi9HF-G/images/Prep-Course-Certified-Backstage-Associate-CBA-Certification/Backstage-Basics/Demo-Backstage-Configuration/chrome-connection-refused-error.jpg?fit=max&auto=format&n=cTFshlV6LNi9HF-G&q=85&s=ca205b29346da9de482d2c2236700cd4" alt="A Chrome browser window displaying a &#x22;This site can't be reached&#x22; error (147.182.170.10 refused to connect, ERR_CONNECTION_REFUSED) with a reload button and troubleshooting suggestions. The address bar and several tabs are visible at the top and a teal mouse cursor is near the bottom right." width="1920" height="1080" data-path="images/Prep-Course-Certified-Backstage-Associate-CBA-Certification/Backstage-Basics/Demo-Backstage-Configuration/chrome-connection-refused-error.jpg" />
</Frame>

Initial (problematic) app-config snippet

```yaml theme={null}
app:
  title: Scaffolded Backstage App
  baseUrl: http://localhost:3000

organization:
  name: My Company

backend:
  # Used for enabling authentication, secret is shared by all
  # backend plugins
  # See https://backstage.io/docs/auth/service-to-service-auth for
  # information on the format
  # auth:
  #   keys:
  #     - secret: ${BACKEND_SECRET}
```

Fix: update the shared app-config to use the server IP or DNS
Follow these steps to allow external browser access and correct frontend↔backend communication:

1. Set `app.baseUrl` to the server IP or DNS with port `3000`.
2. Set `backend.baseUrl` to the same server IP or DNS with port `7007`.
3. Configure the backend `listen.host` to `0.0.0.0` (or remove `host` to bind all interfaces) so it accepts external connections.
4. Add the frontend origin to `backend.cors.origin` so the browser is allowed to call the backend.

Example updated `app-config.yaml` for this demo (replace `147.182.170.10` with your server IP or DNS name):

```yaml theme={null}
app:
  title: Scaffolded Backstage App
  baseUrl: http://147.182.170.10:3000

organization:
  name: My Company

backend:
  # Used for enabling authentication, secret is shared by all
  # backend plugins
  # See https://backstage.io/docs/auth/service-to-service-auth for
  # information on the format
  # auth:
  #   keys:
  #     - secret: ${BACKEND_SECRET}
  baseUrl: http://147.182.170.10:7007
  listen:
    host: 0.0.0.0
    port: 7007
  cors:
    origin:
      - http://147.182.170.10:3000

csp:
  connect-src: ["'self'", 'http:', 'https:']
```

Configuration quick reference

| Setting               | Purpose                                               | Example value (demo)         |
| --------------------- | ----------------------------------------------------- | ---------------------------- |
| `app.baseUrl`         | URL advertised to users and used for frontend routing | `http://147.182.170.10:3000` |
| `backend.baseUrl`     | Backend URL used by frontend for API calls            | `http://147.182.170.10:7007` |
| `backend.listen.host` | Interface the backend binds to                        | `0.0.0.0`                    |
| `backend.cors.origin` | Allowed origins for browser requests                  | `http://147.182.170.10:3000` |

Note: If you have a DNS name for your server, prefer that instead of a raw IP address for better maintainability and SSL/HTTPS compatibility.

Restart the dev server
After changing configuration files you must restart Backstage to load the new settings.

Steps:

1. Stop the running dev server (Ctrl+C in the terminal; press again if necessary to force exit).
2. Start it again:

```bash theme={null}
yarn dev
```

You should see the backend and frontend log successful startup and listening on the configured addresses. After restarting, the frontend should be reachable at `http://147.182.170.10:3000` and be able to communicate with the backend at `http://147.182.170.10:7007`.

Using app-config.local.yaml for local overrides

* Prefer placing machine-specific or development-only overrides in `app-config.local.yaml` so the shared `app-config.yaml` remains suitable across environments.
* Backstage merges configuration files; `app-config.local.yaml` (if present during development) overrides keys in `app-config.yaml`.

Example `app-config.local.yaml` with minimal overrides:

```yaml theme={null}
app:
  baseUrl: http://147.182.170.10:3000

backend:
  baseUrl: http://147.182.170.10:7007
  cors:
    origin:
      - http://147.182.170.10:3000
```

Restore the shared `app-config.yaml` to scaffold defaults for portability:

```yaml theme={null}
app:
  title: Scaffolded Backstage App
  baseUrl: http://localhost:3000

organization:
  name: My Company

backend:
  # Shared defaults; local overrides will take precedence in dev
  baseUrl: http://localhost:7007
  listen:
    port: 7007
```

Because `app-config.local.yaml` has precedence in development, the effective configuration used by `yarn dev` is the merge of `app-config.yaml` and the local overrides. After creating or modifying `app-config.local.yaml`, restart the dev server.

<Callout icon="lightbulb" color="#1CB2FE">
  Use `app-config.local.yaml` for developer- or machine-specific settings (like using an external IP for a demo server). Keep `app-config.yaml` for defaults shared among environments, and use production-specific files (e.g., `app-config.production.yaml`) when deploying to production.
</Callout>

Security reminder

<Callout icon="warning" color="#FF6B6B">
  Exposing a development server on a public IP can expose sensitive endpoints or secrets. Avoid using production secrets in development configs and secure access with firewall rules or VPNs when demoing externally.
</Callout>

Troubleshooting checklist

* Confirm the server firewall/security group allows ports `3000` and `7007`.
* Verify Backstage logs show the backend listening on `0.0.0.0:7007` (or expected host/port).
* Inspect browser DevTools Console and Network tab for CORS errors; adjust `backend.cors.origin` as needed.
* If the frontend still advertises `localhost`, clear caches and ensure `yarn dev` restarted after config changes.

Summary

* The frontend refused external connections because `app.baseUrl` was set to `localhost`. The backend may also have been bound only to localhost or rejected cross-origin requests.
* Update `app.baseUrl`, `backend.baseUrl`, and `backend.cors.origin` to the server IP or DNS name and ensure the backend binds to an interface that accepts external connections.
* Prefer `app-config.local.yaml` for local overrides to keep shared configuration environment-agnostic.
* Always restart Backstage after modifying configuration and double-check firewall and CORS settings.

References and further reading

* Backstage Configuration: [https://backstage.io/docs/configuration/overview](https://backstage.io/docs/configuration/overview)
* Backstage Dev Mode: [https://backstage.io/docs/development/dev-setup](https://backstage.io/docs/development/dev-setup)
* CORS and browser security: [https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/certified-backstage-associate-cba/module/fcbbf923-69c3-4147-bd51-18db2bd18957/lesson/2093a42d-0510-427e-b3c3-2f1e3c932bd4" />
</CardGroup>
