Troubleshooting
Vite bind-host mismatch
Section titled “Vite bind-host mismatch”Some dev servers print a URL like http://localhost:5173, and many projects copy that port directly into devhost.toml. With Vite-style setups, localhost and 127.0.0.1 can point at different listeners, which makes the routed hostname and the printed dev-server URL appear to disagree.
On some machines, though, http://localhost:5173 and http://127.0.0.1:5173 do not hit the same listener:
localhostmay resolve to::1devhostdefaultsbindHostto127.0.0.1- a routed hostname such as
https://app.localhostwill therefore proxy to127.0.0.1:<port>unless you overridebindHost
That can produce confusing behavior where the direct printed localhost URL works, but the routed *.localhost hostname lands on a different local process or response.
When devhost detects that mismatch, it logs an explicit startup warning.
For Vite-style apps that are actually listening on IPv6 loopback, set bindHost = "::1" explicitly:
[services.app]command = ["bun", "run", "dev"]cwd = "."port = 5173bindHost = "::1"host = "app.localhost"If you are unsure which listener your app is using, compare these directly:
curl -I http://localhost:5173/curl -I http://127.0.0.1:5173/curl -I http://[::1]:5173/If those responses differ, set bindHost explicitly instead of relying on the default.
Composite services
Section titled “Composite services”Some “one command” dev scripts are really wrappers that launch multiple long-lived processes, such as a frontend dev server plus an API worker. In those setups, an inherited PORT can miswire child processes if every nested process sees the same value.
By default, devhost injects the configured service port as PORT into that top-level command.
If the wrapper passes its environment through unchanged, every nested child process may inherit the same PORT.
That can produce confusing failures such as:
- one child binding the routed service port even though that port was intended for a different nested process
- another child silently moving to a fallback port after seeing the inherited
PORTalready in use - the frontend proxying
/apito its usual target while the API actually bound somewhere else - routed requests returning backend
404s even though the main page appears to load normally
If your manifest service launches multiple dev processes under one command, prefer splitting them into separate devhost services.
If you intentionally keep a composite wrapper, set injectPort = false on that service and configure the underlying processes explicitly instead:
[services.app]command = ["bun", "run", "dev"]cwd = "."port = 5173injectPort = falsehost = "app.localhost"