Bind the production web server to a chosen port

react-router-serve only honours a port it is given: with PORT unset it calls
getAvailablePort(3000) and takes the first free port from 3000 upwards
(@react-router/serve cli.js). Nothing in the manual path set PORT, so a
busy 3000 silently moved the site to 3001, 3002 and so on, and a reverse
proxy configured against one upstream stopped matching. Docker never showed
this because apps/web/Dockerfile and docker-compose.yml both set PORT=3000.

The web start script now always passes a port - PORT if exported, else
WEB_PORT from .env, else 3000 - so the manual path is as deterministic as
the containerised one and .env's WEB_PORT finally reaches the process that
binds. HOST is deliberately left undefaulted: react-router-serve reads it
directly, unset means every interface (what the container needs) and
127.0.0.1 restricts the socket to a proxy on the same host.

Verified against the existing build: PORT=8123 listens on *:8123, and
WEB_PORT=8124 HOST=127.0.0.1 listens on 127.0.0.1:8124 and serves /en with
a 200 while the CMS is down.

.env.example documents both variables, and the deployment section of
README.md gains a systemd unit, since nothing sources .env there, and a
warning that WEB_PORT is the published host port under Docker but the
listen port in manual mode.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-20 11:33:02 +02:00
co-authored by Claude Opus 5
parent 2c51c35fe8
commit e27e31bfa5
3 changed files with 54 additions and 2 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
"scripts": {
"build": "react-router build",
"dev": "react-router dev --host",
"start": "react-router-serve ./build/server/index.js",
"start": "PORT=${PORT:-${WEB_PORT:-3000}} react-router-serve ./build/server/index.js",
"typecheck": "react-router typegen && tsc"
},
"dependencies": {