From 5c638dbf9770a54cec862690a72912690c58c3da Mon Sep 17 00:00:00 2001 From: Gibson Chikafa Date: Sun, 14 Jun 2026 20:42:08 +0200 Subject: [PATCH 1/3] Update app docs for root-based routing Document the new Python app routing model in the Hopsworks app guide.\n\n- make root-based routing the default for new Streamlit and custom apps\n- describe the UI proxy path mode and readiness probe settings\n- mark APP_BASE_URL_PATH as legacy-only for migration\n- explain the browser mount prefix and the forwarded prefix header\n- update the app details page to show proxy path and readiness settings --- docs/user_guides/projects/apps/index.md | 68 ++++++++++++++++--------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/docs/user_guides/projects/apps/index.md b/docs/user_guides/projects/apps/index.md index 3ef1eab424..f7864b9689 100644 --- a/docs/user_guides/projects/apps/index.md +++ b/docs/user_guides/projects/apps/index.md @@ -30,11 +30,13 @@ The Apps page lists each app with its name, owner, state, UI link, uptime, and a ## Creating an app -The create dialog lets you choose the app type, source, runtime environment, resources, monitoring, and per-app environment variables. +The create dialog lets you choose the app type, source, runtime environment, proxy path mode, readiness probe path, resources, monitoring, and per-app environment variables. | Setting | Typical value | | --- | --- | | App type | `STREAMLIT` or `CUSTOM` | +| Proxy path mode | `ROOT` for new apps, `PREFIX` only while migrating a legacy app | +| Readiness probe path | Leave empty to use the platform default | | Source | Project file or Git repository | | Environment | `python-app-pipeline` | | Memory | `2048` MB | @@ -47,6 +49,9 @@ The create dialog lets you choose the app type, source, runtime environment, res - **Custom** apps are any web service that listens on `APP_PORT`. Common choices are Flask, FastAPI, Gradio, and JavaScript frameworks such as Express. +New apps should use root routing. +Legacy prefix mode is only for older apps that still depend on `APP_BASE_URL_PATH`. + ### App sources - **Project file** means a file already stored in HopsFS or the project file browser. @@ -58,6 +63,24 @@ For Streamlit apps, a project file must be a `.py` file. For Git-backed Streamlit apps, you also need to provide the entrypoint script relative to the repository root. For custom apps, the entrypoint command is required and the app file is optional. +### Routing and readiness + +The browser URL for every app is the proxy mount point under `/hopsworks-api/pythonapp///`. + +New apps should be written as root-based apps and should not depend on `APP_BASE_URL_PATH`. + +Legacy prefix mode is only for existing apps that are still being migrated. + +Hopsworks forwards `X-Forwarded-Prefix` for frameworks that need to generate absolute links. + +Readiness is separate from browser routing. + +Streamlit defaults to `/_stcore/health`. + +Custom apps default to `/`. + +You can override the readiness probe path in the app settings dialog or API when needed. + ### Example structure Most apps follow the same pattern: @@ -77,12 +100,15 @@ Most apps follow the same pattern: ### Streamlit apps Streamlit apps are launched with `streamlit run` behind the Hopsworks proxy. +Hopsworks manages the mount prefix for you, so Streamlit apps can stay root-based. If the app is Git-backed, the entrypoint script is relative to the repository root. +The default readiness probe for Streamlit is `/_stcore/health`. ### Custom apps Custom apps should bind to `0.0.0.0` and use the injected `APP_PORT`. -If the app needs to build links back into itself, use `APP_BASE_URL_PATH` so the paths include the Hopsworks proxy prefix. +Define routes at `/` and `/health`. +Use legacy prefix-aware routes only while migrating an older app that still depends on `APP_BASE_URL_PATH`. ```python import os @@ -91,46 +117,37 @@ import uvicorn from fastapi import FastAPI -APP_BASE_URL_PATH = os.getenv("APP_BASE_URL_PATH", "").rstrip("/") app = FastAPI() -@app.get(f"{APP_BASE_URL_PATH}/health") +@app.get("/health") def health(): return {"status": "ok"} -@app.get(f"{APP_BASE_URL_PATH}/") +@app.get("/") def home(): - return {"status": "ready", "base_path": APP_BASE_URL_PATH or "/"} + return {"status": "ready"} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=int(os.environ["APP_PORT"])) ``` -### Proxy-aware paths +### Root-based routing -`APP_BASE_URL_PATH` is the proxy prefix that Hopsworks mounts your app under, for example: +`APP_BASE_URL_PATH` is deprecated and should not be used in new apps. -`/hopsworks-api/pythonapp//` +For new apps, use root-based routes and let Hopsworks handle the browser mount prefix. -Use it whenever your app defines routes, health checks, or links that point back to itself. -The value may be empty in local development, so a common pattern is to strip the trailing slash and fall back to `/` when rendering content. +Examples for root-based apps: -```python -APP_BASE_URL_PATH = os.getenv("APP_BASE_URL_PATH", "").rstrip("/") -base = APP_BASE_URL_PATH or "/" -``` +- FastAPI: `@app.get("/health")` +- Flask: `Blueprint(..., url_prefix="/")` +- Gradio: `demo.launch(..., root_path=None)` +- Express: `app.use("/", router)` -Examples from Git-backed apps: - -- FastAPI: `@app.get(f"{APP_BASE_URL_PATH}/health")` -- Flask: `Blueprint(..., url_prefix=APP_BASE_URL_PATH or None)` -- Gradio: `demo.launch(..., root_path=APP_BASE_URL_PATH or None)` -- Express: `app.use(APP_BASE_URL_PATH || "/", router)` - -If you hardcode `/` in those places, the app may work locally but break behind the Hopsworks proxy because the browser requests must include the proxy prefix. +If you are migrating an older app that still depends on `APP_BASE_URL_PATH`, keep the legacy pattern only until the app code can move to root-based routing. See the matching examples in [appshopsworkstests](https://github.com/gibchikafa/appshopsworkstests). @@ -141,7 +158,7 @@ Per-app environment variables are applied every time the app starts. The platform injects app-specific variables such as: -- `APP_BASE_URL_PATH` +- `APP_BASE_URL_PATH` for legacy prefix-mode apps only - `APP_PORT` - `STREAMLIT_BASE_URL_PATH` - `STREAMLIT_PORT` @@ -152,6 +169,8 @@ The platform injects app-specific variables such as: Git-backed apps also receive the Git URL, provider, branch, and Streamlit entrypoint script. +`APP_BASE_URL_PATH` is only injected for legacy prefix-mode apps during migration. + Some runtime names are reserved by the platform and cannot be overridden in the UI. That includes the app-path and proxy-path variables above, plus other platform-managed names that start with `HOPS_`, `HOPSWORKS_`, `HOPSFS_`, or `AGENT_`. @@ -178,6 +197,7 @@ The details page includes: - App details, type, and description - App URL +- Proxy path mode and readiness probe path - Public access status for Streamlit apps - Git source details, if the app is Git-backed - Monitoring configuration From 4dedcd753238292f82f9b5306c648bea0b2ee345 Mon Sep 17 00:00:00 2001 From: Gibson Chikafa Date: Sun, 14 Jun 2026 22:55:17 +0200 Subject: [PATCH 2/3] Document app base path routing and readiness - Update the apps guide to use app base path terminology - Describe how Hopsworks mounts apps under / or a user-chosen subpath - Clarify the readiness path behavior and keep APP_BASE_URL_PATH as migration-only guidance --- docs/user_guides/projects/apps/index.md | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/user_guides/projects/apps/index.md b/docs/user_guides/projects/apps/index.md index f7864b9689..0c1f0d53ea 100644 --- a/docs/user_guides/projects/apps/index.md +++ b/docs/user_guides/projects/apps/index.md @@ -30,12 +30,12 @@ The Apps page lists each app with its name, owner, state, UI link, uptime, and a ## Creating an app -The create dialog lets you choose the app type, source, runtime environment, proxy path mode, readiness probe path, resources, monitoring, and per-app environment variables. +The create dialog lets you choose the app type, source, runtime environment, app base path, readiness probe path, resources, monitoring, and per-app environment variables. | Setting | Typical value | | --- | --- | | App type | `STREAMLIT` or `CUSTOM` | -| Proxy path mode | `ROOT` for new apps, `PREFIX` only while migrating a legacy app | +| App base path | `/` for the app root, `/myapp` for a subpath | | Readiness probe path | Leave empty to use the platform default | | Source | Project file or Git repository | | Environment | `python-app-pipeline` | @@ -49,8 +49,9 @@ The create dialog lets you choose the app type, source, runtime environment, pro - **Custom** apps are any web service that listens on `APP_PORT`. Common choices are Flask, FastAPI, Gradio, and JavaScript frameworks such as Express. -New apps should use root routing. -Legacy prefix mode is only for older apps that still depend on `APP_BASE_URL_PATH`. +Use `App base path` to choose where Hopsworks mounts the app. +Set it to `/` for a root-based app or `/myapp` for a subpath. +Legacy prefix routing is only for older apps that still depend on `APP_BASE_URL_PATH`. ### App sources @@ -65,11 +66,10 @@ For custom apps, the entrypoint command is required and the app file is optional ### Routing and readiness -The browser URL for every app is the proxy mount point under `/hopsworks-api/pythonapp///`. +The browser URL for every app is the public mount point under `/hopsworks-api/pythonapp///`. +If you set `App base path` to `/myapp`, the full public URL becomes `/hopsworks-api/pythonapp///myapp/`. -New apps should be written as root-based apps and should not depend on `APP_BASE_URL_PATH`. - -Legacy prefix mode is only for existing apps that are still being migrated. +Hopsworks strips the public mount prefix before forwarding requests upstream, so app code can stay root-based. Hopsworks forwards `X-Forwarded-Prefix` for frameworks that need to generate absolute links. @@ -134,13 +134,13 @@ if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=int(os.environ["APP_PORT"])) ``` -### Root-based routing +### App base path `APP_BASE_URL_PATH` is deprecated and should not be used in new apps. -For new apps, use root-based routes and let Hopsworks handle the browser mount prefix. +For new apps, use `App base path` in the UI or API and let Hopsworks handle the browser mount prefix. -Examples for root-based apps: +Examples: - FastAPI: `@app.get("/health")` - Flask: `Blueprint(..., url_prefix="/")` @@ -172,7 +172,7 @@ Git-backed apps also receive the Git URL, provider, branch, and Streamlit entryp `APP_BASE_URL_PATH` is only injected for legacy prefix-mode apps during migration. Some runtime names are reserved by the platform and cannot be overridden in the UI. -That includes the app-path and proxy-path variables above, plus other platform-managed names that start with `HOPS_`, `HOPSWORKS_`, `HOPSFS_`, or `AGENT_`. +That includes the app-path and routing variables above, plus other platform-managed names that start with `HOPS_`, `HOPSWORKS_`, `HOPSFS_`, or `AGENT_`. ## Managing an app @@ -197,7 +197,7 @@ The details page includes: - App details, type, and description - App URL -- Proxy path mode and readiness probe path +- App base path and readiness probe path - Public access status for Streamlit apps - Git source details, if the app is Git-backed - Monitoring configuration From 68f2c9795d87450466d51364e2d083cceec17ef2 Mon Sep 17 00:00:00 2001 From: Gibson Chikafa Date: Mon, 15 Jun 2026 12:03:47 +0200 Subject: [PATCH 3/3] Document legacy Python app migration guidance --- docs/user_guides/projects/apps/index.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/user_guides/projects/apps/index.md b/docs/user_guides/projects/apps/index.md index 0c1f0d53ea..221db889f0 100644 --- a/docs/user_guides/projects/apps/index.md +++ b/docs/user_guides/projects/apps/index.md @@ -35,6 +35,7 @@ The create dialog lets you choose the app type, source, runtime environment, app | Setting | Typical value | | --- | --- | | App type | `STREAMLIT` or `CUSTOM` | +| Proxy routing mode | `Root routing` for new apps, `Compatibility prefix` for legacy apps | | App base path | `/` for the app root, `/myapp` for a subpath | | Readiness probe path | Leave empty to use the platform default | | Source | Project file or Git repository | @@ -52,6 +53,8 @@ The create dialog lets you choose the app type, source, runtime environment, app Use `App base path` to choose where Hopsworks mounts the app. Set it to `/` for a root-based app or `/myapp` for a subpath. Legacy prefix routing is only for older apps that still depend on `APP_BASE_URL_PATH`. +Use `Proxy routing mode` to switch between `Root routing` and `Compatibility prefix`. +The compatibility mode is only for migrating older apps. ### App sources @@ -73,6 +76,9 @@ Hopsworks strips the public mount prefix before forwarding requests upstream, so Hopsworks forwards `X-Forwarded-Prefix` for frameworks that need to generate absolute links. +The proxy routing mode controls whether Hopsworks strips that prefix or preserves the legacy browser path. +Open **App Settings** and change `Proxy routing mode` under **App routing and readiness** to switch an app. + Readiness is separate from browser routing. Streamlit defaults to `/_stcore/health`. @@ -139,6 +145,7 @@ if __name__ == "__main__": `APP_BASE_URL_PATH` is deprecated and should not be used in new apps. For new apps, use `App base path` in the UI or API and let Hopsworks handle the browser mount prefix. +Keep `Proxy routing mode` set to `Root routing` for new apps. Examples: @@ -148,6 +155,7 @@ Examples: - Express: `app.use("/", router)` If you are migrating an older app that still depends on `APP_BASE_URL_PATH`, keep the legacy pattern only until the app code can move to root-based routing. +In that case, use `Compatibility prefix` in the app settings dialog during the migration period. See the matching examples in [appshopsworkstests](https://github.com/gibchikafa/appshopsworkstests). @@ -197,7 +205,7 @@ The details page includes: - App details, type, and description - App URL -- App base path and readiness probe path +- App base path, proxy routing mode, and readiness probe path - Public access status for Streamlit apps - Git source details, if the app is Git-backed - Monitoring configuration