Fix nginx buffering the webcam stream into RAM on the fluidd interface#38
Open
arlophoenix wants to merge 2 commits into
Open
Fix nginx buffering the webcam stream into RAM on the fluidd interface#38arlophoenix wants to merge 2 commits into
arlophoenix wants to merge 2 commits into
Conversation
…nsail The mainsail server block sets postpone_output 0, proxy_buffering off and proxy_ignore_headers X-Accel-Buffering on every /webcam*/ location; the fluidd block sets none of them. With buffering left on, nginx spools an MJPEG stream a client cannot drain fast enough into its proxy temp directory. That directory is the compiled default /var/tmp/nginx/proxy, and on CrealityOS /var/tmp is a symlink to /tmp, which is tmpfs -- so the spill lands in RAM. This only makes the file consistent with itself; the mainsail block is unchanged.
proxy_max_temp_file_size defaults to 1024 MB. On these boxes the proxy temp path resolves into a ~100 MB tmpfs on a ~200 MB machine, so the default is an invitation to exhaust RAM. Setting it to 0 makes nginx pass proxied responses through synchronously and never write a temp file, so a slow client throttles the upstream instead of consuming memory. This covers every proxied response, not just the webcam -- including large /server/files/... gcode reads, which are routinely ~100 MB. Deliberately not relocating proxy_temp_path to /usr/data: that would trade RAM exhaustion for continuous eMMC writes on every stream.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The fluidd server block in
files/moonraker/nginx.confproxies/webcam*/withoutproxy_buffering off, while the mainsail block in the same file has it on all four locations. Because nginx's compiledproxy_temp_pathis/var/tmp/nginx/proxyand/var/tmpis a symlink to/tmp, which is tmpfs, a client that drains the MJPEG stream slower than the camera produces it makes nginx spool the difference into RAM. The defaultproxy_max_temp_file_sizeis 1024 MB against a ~100 MB tmpfs on a ~200 MB machine, so nothing bounds it.On a K1C 2025 this exhausted memory in about two minutes mid-print. Shmem went from a flat 11.8 MB baseline to 93.5 MB in ninety seconds and plateaued at the tmpfs cap; the kernel evicted the entire buffer cache, moonraker logged a 7.17 second blocked event loop, klipper lost communication with
nozzle_mcu, power-loss recovery auto-resumed the print, and the OOM killer took moonraker. That MCU's retransmit counter sat at exactly 9 for the preceding two hours and has been 0 since, so the link was fine — the host was starved. The OOM dump attributes 93,796 kB to shmem that no process mapped, which is unmapped tmpfs file data. The same signature recurred on a second date peaking at 91,400 kB.This is not confined to port 4408.
scripts/creality_web_interface.shinsertslisten 80;afterlisten 4408 default_server;when the user selects fluidd as the port-80 interface, so on those installs the unprotected block is what answers a barehttp://<printer>/webcam/.The first commit gives the four fluidd
/webcam*/locations the same five directives the mainsail block already carries — it only makes the file consistent with itself, and the mainsail block is untouched. The second setsproxy_max_temp_file_size 0in thehttpblock as a backstop, so nginx passes proxied responses through synchronously and never writes a temp file; a slow client then throttles the upstream instead of consuming RAM. The two are separable if you want only the first. Pointingproxy_temp_pathat/usr/datawould also stop the RAM growth, but trades it for continuous eMMC writes on every stream, so it is deliberately not the fix here.Verified on a K1C 2025 (mid-print) and a K1C OG (standby): staged
nginx -tbefore and after install, gracefulnginx -s reloadwith the master PID unchanged both times so klipper and moonraker were never interrupted, then fluidd 200 on 4408 (and on port 80 for the OG), moonraker API 200, a ~71 KB snapshot and 5.3 MB of stream in 5 s.Test plan
grep -c "proxy_buffering off" files/moonraker/nginx.confreturns 8; diff is 21 insertions and 0 deletions, so the mainsail block is provably unchangednginx -tpasses on the rendered config, before and after install/tmpusage andShmemstayed flat, swap untouched, zero files created under/tmp/nginx/proxyNot addressed
The same unbounded-buffering exposure applies to any large proxied response, notably
/server/files/...gcode downloads that routinely run ~100 MB; theproxy_max_temp_file_size 0backstop covers those generically rather than being webcam-specific. Separately, moonraker has no restart supervision on these boxes, so an OOM kill from any cause leaves the web UI down until reboot — out of scope here.