Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

Custom/Generic app

Updated 2 min read

You can create and deploy arbitrary apps with jhub-apps, including Python apps, Node.js servers, Go applications, Ruby apps, or any executable that can run as a web server.

Your conda environment (used in JHub Apps Launcher’s App deployment form) should have:

  • The runtime/executable for your app (Python, Node.js, Go, Ruby, etc.)
  • Custom framework used for the app
  • Other libraries/dependencies used in the app

Note: jhsingle-native-proxy is no longer required - JHub Apps now uses jhub-app-proxy which is automatically installed when deploying custom apps.

In the JHub Apps Launcher App deployment form, select “Custom Command” for the “Framework”. A text field input for a “Custom Command” is displayed.

custom app form fields

The Custom Command field accepts the complete command to run your application. The command should be exactly as you would run it, with the following consideration:

  • Use {port} as a placeholder for the port number - JHub Apps will substitute this with the actual port allocated by JupyterHub

Python HTTP server:

Terminal window
python -m http.server {port}

Flask application:

Terminal window
python -m flask run --port {port} --host 0.0.0.0

Python script:

Terminal window
python /path/to/app.py {port}

Custom Python module:

Terminal window
python -c "from mymodule import app; app.run(port={port})"

Node.js server:

Terminal window
node server.js --port {port}

Go application:

Terminal window
./my-go-app --port {port}

Ruby server:

Terminal window
ruby app.rb -p {port}

Shell script:

Terminal window
bash /path/to/start.sh {port}

You can set environment variables for your custom app through:

  1. Via the UI: Add environment variables in the app creation form
  2. Via configuration: Set environment variables in startup_apps configuration

Example using startup_apps:

c.JAppsConfig.startup_apps = [
{
"username": "my-user",
"servername": "my-custom-app",
"user_options": {
"display_name": "My Custom App",
"filepath": "app.py",
"framework": "custom",
"custom_command": "python app.py {port}",
"env": {
"MY_VAR": "value",
"DEBUG": "true"
},
"conda_env": "my-env"
}
}
]

You can override or add additional arguments for the jhub-app-proxy CLI using the JHUB_APP_PROXY_ARGS environment variable. This allows you to customize health checks, timeouts, logging, and other proxy behavior on a per-app basis.

Via the UI:

Add an environment variable in the app creation form’s Environment Variables section:

Proxy argument overrides

Via configuration:

c.JAppsConfig.startup_apps = [
{
"username": "my-user",
"servername": "my-app",
"user_options": {
"display_name": "My App",
"framework": "streamlit",
"filepath": "app.py",
"env": {
"JHUB_APP_PROXY_ARGS": "--ready-check-path=/health --ready-timeout=600"
}
}
}
]

Common arguments:

  • --strip-prefix: Controls whether the JupyterHub service prefix is stripped before forwarding requests to your app (default: true)
    • When to use true (default): Most apps (Streamlit, Panel, Gradio, etc.) expect requests without the JupyterHub prefix
      • Example: /user/admin/my-app/index.html → forwards as /index.html
    • When to use false: Apps that handle their own base URL configuration (like JupyterLab with ServerApp.base_url)
      • Example: To override for a custom app that manages its own base URL:
        JHUB_APP_PROXY_ARGS="--strip-prefix=false"
  • --ready-check-path: Health check endpoint path (default: /)
  • --ready-timeout: Health check timeout in seconds (default: 300)
  • --log-level: Log level - debug, info, warn, error (default: info)
  • --log-format: Log format - json, pretty (default: json)

For a complete list of available arguments, see the jhub-app-proxy documentation.

Duplicate argument handling:

If an argument is already set by the framework configuration (e.g., --ready-check-path=/ for Gradio), providing it via JHUB_APP_PROXY_ARGS will override the default value. The environment variable takes precedence.

You can specify a custom version of jhub-app-proxy to use:

Per-app (via environment variable):

"env": {
"JHUB_APP_PROXY_VERSION": "v0.6"
}

Globally (in jupyterhub_config.py):

c.JAppsConfig.jhub_app_proxy_version = "v0.6"

Priority: App-specific environment variable > Global config > Default