fix(nginx): use checkout-local temp paths for local development (#8075)

* fix(nginx): use checkout-local temp paths for local development

* docs(nginx): clarify local request buffering comment

* docs: keep nginx guidance within the inherited byte budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
zuiqiangxijiejin 2026-11-10 09:47:37 +08:00 committed by GitHub
parent c612531213
commit ea7bb64f02
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 45 additions and 20 deletions

View File

@ -25,24 +25,23 @@ A single `make dev` / Docker stack runs four cooperating services:
| **Frontend** | `3000` | Next.js web interface |
| **Provisioner** | `8002` | Optional — only when sandbox is configured for provisioner/K8s mode |
Nginx is the single public entry: it proxies `/api/*` to the Gateway, rewriting
`/api/langgraph/*` onto the Gateway's native routes, and serves the frontend — see
[backend/AGENTS.md](backend/AGENTS.md) for the runtime and router detail. It compresses
HTML and configured textual assets, deliberately leaving SSE, fonts, images, audio, and
video uncompressed at the proxy layer.
Nginx serves the frontend and proxies `/api/*` to Gateway, rewriting
`/api/langgraph/*` to native routes. See [backend/AGENTS.md](backend/AGENTS.md).
It compresses HTML and configured text assets. SSE, fonts, images, audio, and
video stay uncompressed.
Keep the dedicated `/api/threads/{thread_id}/browser/stream` WebSocket regex before
the generic threads regex in Docker, local and Helm nginx configs. Upgrade headers
and the 600s read timeout are pinned by `backend/tests/test_nginx_browser_websocket.py`.
Helm uses nginx's default connect/send timeouts for the in-cluster Gateway.
Put the `/api/threads/{thread_id}/browser/stream` WebSocket regex before the
generic threads regex in Docker, local, and Helm configs.
`backend/tests/test_nginx_browser_websocket.py` pins upgrade headers and the
600s read timeout. Helm uses nginx defaults for Gateway connect/send timeouts.
Keep local temp paths checkout-relative and aligned with `serve.sh` and `nginx.sh`.
Both Compose files publish nginx at `"${BIND_HOST:-127.0.0.1}:${PORT:-2026}:2026"`
(README's **loopback default**); bare `"${PORT}:2026"` binds `0.0.0.0`. Root `PORT`
controls Docker ingress only; local Next.js stays on `3000` so `.env` cannot
redirect `make dev`'s port wait. Nginx's IPv4+IPv6 `default_server` and Gateway's
`0.0.0.0:8001` bind are container-internal; only published nginx is external.
Require explicit bind addresses for new published ports;
`backend/tests/test_compose_default_bind_host.py` pins every service in both files.
Both Compose files publish nginx at `"${BIND_HOST:-127.0.0.1}:${PORT:-2026}:2026"`.
Bare `"${PORT}:2026"` binds `0.0.0.0`. Require explicit bind addresses for new
published ports. `backend/tests/test_compose_default_bind_host.py` pins both files.
Root `PORT` controls Docker ingress only. Local Next.js stays on `3000` so
`.env` cannot redirect the dev port wait. Nginx's IPv4/IPv6 `default_server`
and Gateway's `0.0.0.0:8001` stay container-internal. Only published nginx is external.
Provisioner PVC mode declares `USERDATA_PVC_NAME` once as `user-data`; extra and
Lark broker credential mounts reuse it with their existing subPaths and read-only

View File

@ -941,6 +941,8 @@ such a checkout, use `bash ./scripts/<name>.sh ...`.
make dev
```
Local nginx writes temporary files under the checkout's `temp/` directory.
5. **Access**: http://localhost:2026
6. **(Optional) Load sample memory data for local review**: open `Settings > Memory`, click **Import memory**, and select `backend/docs/memory-settings-sample.json`. The browser imports into the signed-in user's memory.

View File

@ -0,0 +1,18 @@
"""Local nginx must use the writable directories prepared by its launchers."""
import re
import pytest
from support.nginx_conf import REPO_ROOT, read_config
@pytest.mark.parametrize("kind", ["client_body", "proxy", "fastcgi", "uwsgi", "scgi"])
def test_local_nginx_temp_path_matches_launchers(kind: str) -> None:
directory = f"temp/{kind}_temp"
config = read_config("docker/nginx/nginx.local.conf")
paths = re.findall(rf"^[ \t]*{kind}_temp_path[ \t]+([^;\n]+);", config, re.M)
assert paths == [directory]
for launcher in ("scripts/serve.sh", "scripts/nginx.sh"):
lines = (REPO_ROOT / launcher).read_text(encoding="utf-8").splitlines()
assert any(line.startswith("mkdir -p ") and directory in line.split()[2:] for line in lines), f"{launcher} must create {directory} before starting nginx"

View File

@ -7,6 +7,12 @@ events {
}
pid logs/nginx.pid;
http {
client_body_temp_path temp/client_body_temp;
proxy_temp_path temp/proxy_temp;
fastcgi_temp_path temp/fastcgi_temp;
uwsgi_temp_path temp/uwsgi_temp;
scgi_temp_path temp/scgi_temp;
# Basic settings
sendfile on;
tcp_nopush on;
@ -114,8 +120,8 @@ http {
# Long chat/text-prompt support (issue #3952): a sufficiently long
# pasted prompt otherwise exceeds nginx's default 1m
# client_max_body_size, or gets spooled to a temp file via
# proxy_request_buffering before reaching Gateway -- on a non-root
# local run that temp directory may not be writable, producing a
# proxy_request_buffering before reaching Gateway. Previously, an
# unwritable system temp directory on non-root runs could cause a
# raw nginx 500 instead of a graceful application error. Mirrors
# the uploads location's fix below (same mechanism, sized for text
# prompts rather than binary file uploads).
@ -397,8 +403,8 @@ http {
# Disable response buffering for the frontend. Without this,
# nginx tries to spool large upstream responses (e.g. Next.js
# static chunks) into ``proxy_temp_path``, which defaults to
# the system-owned ``/var/lib/nginx/proxy`` and fails with
# static chunks) into ``proxy_temp_path``. System-owned defaults
# such as ``/var/lib/nginx/proxy`` can fail with
# ``[crit] open() ... failed (13: Permission denied)`` when
# nginx is launched as a non-root user (every dev machine
# except production root containers). The symptom on the