libredb / libredb/libredb-studio

[BUG] npx launcher binds to the inherited HOSTNAME inside a container

Open Beginner friendly
#813 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
726
Forks
119
Avg merge
7h 47m
Merged PRs (30d)
265

Description

## Description

The launcher takes the inherited `HOSTNAME` as the bind address. Inside a container that value is the container id, so the server listens only on the container address and nothing on loopback answers.

`bin/studio.js:310`

```js
if (!env.HOSTNAME) env.HOSTNAME = "127.0.0.1";
```

The loopback default only applies when `HOSTNAME` is empty. Docker and Kubernetes always set it to the container id or pod name, so the default never runs there.

This is a listening problem, not a display problem. The separate defect where the boot banner prints `localhost` regardless of the bind address is #814; fixing that one alone would leave this one in place and only make it harder to notice.

## Steps to Reproduce

Run `main`'s launcher against a released payload so no build is needed:

```
git clone --depth=1 https://github.com/libredb/libredb-studio
curl -sSLO https://github.com/libredb/libredb-studio/releases/download/0.15.0/libredb-studio-standalone-0.15.0-linux-arm64.tar.gz

docker run -d --name t -v "$PWD:/app" -w /app node:24-alpine \
sh -c 'LIBREDB_STUDIO_SKIP_PROVENANCE=1 node libredb-studio/bin/studio.js \
--archive /app/libredb-studio-standalone-0.15.0-linux-arm64.tar.gz > /tmp/l.log 2>&1'

docker exec t sh -c 'apk add -q curl
echo "HOSTNAME=$HOSTNAME"
curl -s -o /dev/null -w "localhost %{http_code}\n" http://localhost:3000/login
curl -s -o /dev/null -w "container %{http_code}\n" "http://$(hostname):3000/login"'
```

## Expected Behavior

The server listens on loopback, as the `--host` help text and `docs/DISTRIBUTION.md` both state.

## Actual Behavior

Launcher 0.16.0 from `main` (`acf50738`), payload 0.15.0, no environment set by hand:

```
HOSTNAME=46db4676bb04
Starting LibreDB Studio 0.16.0 on http://46db4676bb04:3000

localhost 000
127.0.0.1 000
46db4676bb04 200
```

Three runs isolate the cause. Only the bind address changes between them.

| `HOSTNAME` | Bound to | `localhost` | container address |
| --- | --- | --- | --- |
| inherited (container id) | container id | **000** | 200 |
| `127.0.0.1` | 127.0.0.1 | 200 | 000 |
| `0.0.0.0` | all interfaces | 200 | 200 |

`--host 127.0.0.1` behaves the same as the second row, so the flag is the only thing standing between the user and a working loopback.

## Environment

- **LibreDB Studio Version**: launcher `main` (`acf50738`, reports 0.16.0); payload 0.15.0
- **Browser**: not involved, reproduced with curl
- **OS**: `node:24-alpine` container on Docker 29.2.1
- **Node.js/Bun Version**: Node.js 24
- **Database Type**: not involved, the server never becomes reachable

## Additional Context

This reaches further than npx on a laptop. Any host that starts the server in a container and proxies it over loopback gets a dead port: a workspace or dev-environment module, a sidecar, a panel that runs the process next to its own proxy. The desktop build has the same shape, where the server runs as a sidecar on a loopback port.

I checked the other channels before filing, and the scope is otherwise narrow.

- The official image is fine. `Dockerfile` carries `ENV HOSTNAME=""`, which suppresses the injection, and `docker/bind-address.mjs` resolves the address on every start. `ghcr.io/libredb/libredb-studio:0.15.0` answers on `localhost` from inside its own container, and `0.9.59` answers as well.
- Helm is fine. The chart passes `config.bindAddress`, empty by default, so the image resolver runs. Probes use the pod IP, and `kubectl port-forward` works against the wildcard listener.
- deb, rpm, Homebrew, the Windows launcher and Snap all force `HOSTNAME` to `127.0.0.1` already. That was #134.

So this trap has been closed twice, in #134 for the local packages and in #432 for the image. The npx launcher is the one path that still has it.

The docs also disagree with the behaviour. `docs/DISTRIBUTION.md` lists `npx` with "Default bind: 127.0.0.1", which is true on a normal machine and false inside a container. The same file already describes this exact trap for the deb and rpm wrappers:

> ignores any inherited HOSTNAME (empty, or, under Docker, the container ID Next.js would otherwise bind to) and defaults to loopback

I found this while writing a Coder workspace module that starts the app with npx. The app link was dead in the workspace. Working around it took one line in the module, but the same thing would hit anyone running npx inside a container.

## Possible Solution

Apply the rule the image resolver already uses: treat `HOSTNAME` as an operator choice only when it differs from `os.hostname()`. A container id or pod name is always equal to it, so it would be ignored and the loopback default would apply. An explicit `--host` or a deliberate `HOSTNAME=0.0.0.0` keeps working.

## Logs

```
Starting LibreDB Studio 0.16.0 on http://46db4676bb04:3000
- Local: http://46db4676bb04:3000
- Network: http://46db4676bb04:3000
```

Contributor guide

Open the contributing guide

Research direction

Start at bin/studio.js:310 and compare its HOSTNAME handling with docker/bind-address.mjs. Reproduce the container case from the issue, then verify that inherited container names use loopback while explicit --host values and deliberate HOSTNAME values still work; docs/DISTRIBUTION.md describes the expected default.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
cli
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
82/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.