| # Updates the existing 9hits-viewer Render service so one container runs | |
| # both 9Hits and FeelingSurf AT THE SAME TIME on the free 512 MB plan. | |
| # Do not rename this service — a different Blueprint name would create a | |
| # second deployment instead of updating | |
| # https://dashboard.render.com/web/srv-da09a7dbedkc73a829cg | |
| services: | |
| - type: web | |
| name: 9hits-viewer | |
| runtime: docker | |
| dockerfilePath: ./Dockerfile | |
| plan: free | |
| healthCheckPath: /health | |
| envVars: | |
| # 9Hits access key (hard-coded). FeelingSurf token preconfigured below. | |
| - key: ACCESS_KEY | |
| value: "23f9097a8d823267188c49b3cc0598b1" | |
| - key: ACCESS_TOKEN | |
| value: "27f77ef00e768cdbdc7352f3456e35b5" | |
| - key: PORT | |
| value: "10000" | |
| # --- BOTH viewers, CONCURRENTLY, in 512 MB ----------------------------- | |
| # Measured with a real Chromium 149 (see README "Measured memory"): | |
| # LOW_MEMORY=extreme (both viewers --single-process) -> ~324 MB pair | |
| # 9Hits SP + FeelingSurf swiftshader (if SP crashes FS) -> ~458 MB | |
| # Both fit under the 512 MB budget, so the two viewers run at the same | |
| # time instead of alternating. Layers that make it safe: | |
| # 1. LOW_MEMORY=extreme shrinks both Chromiums hard. | |
| # 2. memguard.py watches total RSS (limit 512 MB, acts at 97% = 497 MB) | |
| # and restarts the heaviest viewer BEFORE the platform can OOM-kill | |
| # the container - a safety valve, not a scheduler. | |
| # 3. DUAL_VIEWER_MODE=concurrent = always run both, never time-slice. | |
| # 4. Crash auto-fallback: if a viewer crash-loops 3x at startup under | |
| # single-process, its SP flags are dropped automatically (it keeps | |
| # running on the balanced set; NH_SP/FS_SP=no force that manually). | |
| # If FeelingSurf specifically fails with swiftshader under SP, set | |
| # FS_GL_MODE=disable-gpu. | |
| - key: NINEHITS_ENABLED | |
| value: "yes" | |
| - key: FEELINGSURF_ENABLED | |
| value: "yes" | |
| - key: DUAL_VIEWER_MODE | |
| value: "concurrent" # both at the same time; auto escalates to time-slice | |
| - key: LOW_MEMORY | |
| value: "extreme" # balanced | extreme | auto | off | |
| - key: NH_SP | |
| value: "yes" # single-process for 9Hits (auto-fallback on crash) | |
| - key: FS_SP | |
| value: "yes" # single-process for FeelingSurf (auto-fallback on crash) | |
| - key: FS_GL_MODE | |
| value: "swiftshader" # swiftshader | disable-gpu (last resort) | |
| - key: MEMGUARD_LIMIT_MB | |
| value: "512" # Render free plan limit | |
| - key: MEMGUARD_HARD_PCT | |
| value: "97" # act at 497 MB so the platform OOM never fires | |
| - key: MEMGUARD_COOLDOWN | |
| value: "120" # min seconds between guardian restarts | |
| - key: CLEAR_ALL_SESSIONS | |
| value: "yes" | |
| - key: SYSTEM_SESSION | |
| value: "yes" | |
| - key: SESSION_NOTE | |
| value: "render-system" | |
| - key: NOTE | |
| value: "render" | |
| - key: HIDE_BROWSER | |
| value: "yes" | |
| - key: ALLOW_POPUPS | |
| value: "no" | |
| - key: ALLOW_ADULT | |
| value: "no" | |
| - key: ALLOW_CRYPTO | |
| value: "no" | |
| - key: CACHE_LIMIT | |
| value: "0" | |
| - key: RESET_INTERVAL | |
| value: "2h" | |
| - key: SUPERVISOR_DELAY | |
| value: "10" | |