Serve the API on 3000 and 8000 at once, matching the frontend image
Dokploy routes the domain to port 3000, but the container only bound 8000, so the proxy had nothing to talk to and the domain returned 502 with a perfectly healthy process behind it. The frontend image already solved this by answering on both 80 and 3000 (`listen 80; listen 3000;` in nginx.conf). Do the same here rather than swap one guess for another: 3000 is what the platform routes to, and 8000 is what the README, the vite dev proxy and docker-compose all target, so binding both means the container works whichever one it is pointed at. uvicorn's CLI takes a single --port, but Server.run() accepts pre-bound sockets, so serve.py binds each port and hands the list to one uvicorn - no extra worker or second process to supervise. PORT still pins a single port for anyone who wants one; PORTS changes the pair. A port that cannot be bound is logged and skipped rather than being fatal, since losing one of the two should not take down a service the platform only routes to on the other. It exits non-zero only when nothing is listening at all, so a genuinely dead container is still reported as failed. The healthcheck moves into the same file and passes if either port answers, which keeps it from drifting out of sync with what is actually bound. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
22
README.md
22
README.md
@@ -69,6 +69,28 @@ permission check; `user` holds the product/store/inventory permissions.
|
||||
`AUTH_ENABLED=false` disables all of it for local work — never in a deployment.
|
||||
See the Authentication section of `../DEPLOYMENT.md` for the full endpoint map.
|
||||
|
||||
## Ports
|
||||
|
||||
The container answers on **3000 and 8000 at the same time**, the same way the
|
||||
frontend image answers on 80 and 3000. 3000 is what Dokploy routes a domain to;
|
||||
8000 is what this README, the vite dev proxy and `docker-compose.yml` use. Both
|
||||
being live means the deployment works whichever one it is pointed at, instead of
|
||||
returning 502 from a healthy container.
|
||||
|
||||
`serve.py` is what makes that possible - uvicorn's CLI binds a single `--port`,
|
||||
but `Server.run()` accepts a list of pre-bound sockets, so it is still one
|
||||
process. If one port is unavailable it logs and carries on with the other; it
|
||||
exits non-zero only when nothing is listening.
|
||||
|
||||
```bash
|
||||
python serve.py # 3000 and 8000
|
||||
PORT=8080 python serve.py # only 8080 (PORT pins a single port)
|
||||
PORTS=80,3000 python serve.py # a different pair
|
||||
```
|
||||
|
||||
For local development `uvicorn app.main:app --reload --port 8000` is still the
|
||||
normal thing to run - one port is all you need, and it gives you autoreload.
|
||||
|
||||
## Persistence: the two volumes a deployment needs
|
||||
|
||||
Most state lives in Postgres, but three things are written to the filesystem,
|
||||
|
||||
Reference in New Issue
Block a user