Skip to content

NiceGUI Configuration And Deployment

Use this reference when a NiceGUI task concerns ui.run(...) settings, runtime URLs, native windows, environment variables, server hosting, executable packaging, or NiceGUI On Air. For startup ownership, app factories, ui.run_with(...), lifespan, reload, and workers, load FastAPI and Uvicorn startup.

The public surfaces below follow NiceGUI's current configuration and deployment documentation. Inspect the target project's pinned NiceGUI version before relying on a recently added option or native-mode behavior.

Configure The Owning Runtime

Choose the process owner before setting runtime options:

Deployment shape Owning surface Where configuration belongs
NiceGUI is the application and starts its server ui.run(...) NiceGUI arguments plus additional Uvicorn keyword arguments
A parent FastAPI app owns startup ui.run_with(parent_app, ...) and the external ASGI server NiceGUI composition options in ui.run_with; socket, TLS, reload, and worker options in Uvicorn or the process manager
Desktop application ui.run(native=True, ...) NiceGUI runtime options and app.native configuration
Packaged browser or desktop executable ui.run(reload=False, ...) import-safe page registration, packaging flags, and multiprocessing setup

Do not split ownership by calling ui.run() and a separate server launcher for the same app. The exact composition patterns and worker constraints are in FastAPI and Uvicorn startup.

Select ui.run Options Deliberately

ui.run(...) accepts several groups of settings:

Concern Representative options Decision rule
route and metadata root, title, viewport, favicon, language, dark, markdown Use a root callable or decorated pages; override metadata per page when it is route-specific.
network and launch host, port, show, on_air Bind and expose only the interfaces required by the deployment; treat On Air as a separate remote-access choice.
client recovery reconnect_timeout, message_history_length Tune together from observed disconnect duration and replay volume; replay is not durable job delivery.
binding work binding_refresh_interval Reduce active links before lowering the interval; use None only when no polling-based links need updates.
static delivery cache_control_directives, gzip_middleware_factory Preserve deliberate cache lifetimes and compression behavior; disabling gzip or changing immutable caching is an operational decision.
development reload, uvicorn_reload_dirs, uvicorn_reload_includes, uvicorn_reload_excludes, uvicorn_logging_level Keep reload local to development and restart fully when changing options that the reloader process owns.
frontend runtime tailwind, unocss, prod_js Verify class compatibility before switching CSS engines; use production Vue and Quasar assets in deployed apps.
API visibility fastapi_docs, endpoint_documentation Expose only the OpenAPI surfaces the application intends to publish.
browser storage storage_secret, session_middleware_kwargs A secret is required for ui.storage.user and ui.storage.browser; load it from a secret source and configure cookie policy for the deployment.
native window native, window_size, fullscreen, frameless Use only for a desktop app with a supported browser engine.

Additional keyword arguments are forwarded to uvicorn.run. Most ui.run option changes require stopping and fully restarting the process; do not assume development auto-reload applies them.

Read Runtime URLs After Binding

app.urls contains the URLs on which the running app is available. The server has not bound its sockets during app.on_startup, so the collection is not available there. Read it in a page function or subscribe to app.urls.on_change when another application component needs the final addresses.

from nicegui import app, ui


@ui.page('/')
def home() -> None:
    for url in app.urls:
        ui.link(url, target=url)


ui.run()

Do not derive a public URL solely from the listening host and port when a reverse proxy, container port mapping, or tunnel owns the external address.

Configure Environment-Controlled Facilities

NiceGUI recognizes these framework environment variables:

Variable Default Effect
MATPLOTLIB enabled Set to false to skip the potentially costly Matplotlib import; ui.pyplot and ui.line_plot then remain unavailable.
NICEGUI_STORAGE_PATH .nicegui in the working directory Changes the local storage-file directory.
NICEGUI_REDIS_URL no Redis backend Selects Redis for shared persistent storage.
NICEGUI_REDIS_KEY_PREFIX nicegui: Namespaces NiceGUI keys in Redis.
MARKDOWN_CONTENT_CACHE_SIZE 1000 Bounds cached Markdown snippets.
RST_CONTENT_CACHE_SIZE 1000 Bounds cached reStructuredText snippets.

Treat these as process-start configuration. For application-owned host, port, credentials, feature flags, and service settings, use one validated settings model rather than scattering direct environment reads. When multiple processes or executables share local storage, do not let them independently rewrite the same files; give each instance a distinct NICEGUI_STORAGE_PATH or configure Redis where state must be shared.

Deploy A Browser-Hosted App

Run the production entry point under a service manager or container restart policy. NiceGUI's multi-architecture Docker image runs an application mounted at /app; its default internal port is 8080, so publish that port explicitly. The image supports non-root execution through PUID and PGID and passes process signals through to the app.

docker run --detach --restart always \
    --publish 80:8080 \
    --env PUID="$(id -u)" \
    --env PGID="$(id -g)" \
    --volume "$PWD:/app" \
    zauberzeug/nicegui:latest

For HTTPS, either pass Uvicorn's ssl_certfile and ssl_keyfile options to ui.run(...) or terminate TLS at a reverse proxy such as NGINX or Traefik. A reverse-proxy deployment must preserve NiceGUI's HTTP and Socket.IO traffic, forwarding scheme and host information, route prefixes, timeouts, and upload limits consistently. Verify the rendered page, static assets, websocket connection, reconnect behavior, and upload path through the public URL rather than only against the container port.

Use one worker by default. NiceGUI clients, element trees, tasks, and ordinary Python state are process-local; a multi-worker deployment needs compatible session affinity and externalized shared state. See FastAPI and Uvicorn startup before adding workers or combining them with reload.

Build A Native Desktop App

ui.run(native=True) launches a pywebview window. window_size, fullscreen, and frameless cover common presentation settings. Configure lower-level pywebview behavior before startup through:

  • app.native.window_args for webview.create_window arguments
  • app.native.start_args for webview.start arguments
  • app.native.settings for pywebview settings
  • app.native.main_window for asynchronous access to the running window

Values in window_args and start_args take precedence over overlapping ui.run arguments. The browser engine must support ES modules and import maps; use Chrome 89 or newer, a current WebKitGTK or Qt backend on Linux, and the EdgeChromium prerequisites used by pywebview on Windows. A local Windows favicon used as the native icon must be an .ico file.

Native mode chooses an available port automatically when port is omitted. Browser mode defaults to 8080; use native.find_open_port() explicitly when multiple browser-mode executable instances must coexist.

Native Events And Process Placement

Register sync or async handlers with app.native.on(...). Supported lifecycle and window events are shown, loaded, minimized, maximized, restored, resized, moved, closed, and drop. Resized and moved events expose dimensions or coordinates in event.args; drop events expose filesystem paths under event.args['files'].

The native UI runs in a separate process. Define app.native.window_args, start_args, settings, and event registrations outside the if __name__ == '__main__': guard so the child process sees them.

from nicegui import app, ui

app.native.window_args['resizable'] = False
app.native.on('drop', lambda event: print(event.args['files']))

if __name__ == '__main__':
    ui.run(native=True, reload=False)

Native storage follows the same scopes as browser mode. Multiple executable instances started from one working directory can collide on the default .nicegui files; isolate NICEGUI_STORAGE_PATH per instance or use Redis for intentionally shared state.

Package An Executable

Both nicegui-pack/PyInstaller and Nuitka require an import-safe application:

  1. Disable auto-reload with ui.run(reload=False, ...).
  2. Supply a root page callable to ui.run or register at least one @ui.page.
  3. Decide whether the executable opens a browser or uses native=True.
  4. Use an available port when simultaneous instances are valid.
  5. Exercise the built artifact on every target operating system; a successful build on the development host does not establish runtime compatibility.

With nicegui-pack, --onefile is convenient but starts more slowly because PyInstaller extracts it on each run. A directory build starts faster and can be archived for distribution. Use --windowed only with native=True; a browser-mode application without a console has no normal Ctrl-C exit surface.

Nuitka must include both NiceGUI modules and package data because NiceGUI uses lazy imports and ships frontend assets:

python -m nuitka \
    --onefile \
    --include-package=nicegui \
    --include-package-data=nicegui \
    main.py

Add equivalent package and package-data flags for optional libraries that ship templates or frontend assets. Prefer --standalone when startup speed matters more than producing one file.

Multiprocessing In Packaged Native Apps

Packaged native apps must call multiprocessing.freeze_support() as the first statement inside the main guard to prevent recursive process creation. Keep native settings outside the guard so the spawned native process applies them.

from multiprocessing import freeze_support

from nicegui import app, ui

app.native.window_args['transparent'] = True


def root() -> None:
    ui.label('Packaged app')


if __name__ == '__main__':
    freeze_support()
    ui.run(root, native=True, reload=False)

Use On Air Only For Deliberate Remote Access

ui.run(on_air=True) creates a temporary public URL, currently valid for one hour. A private device token can select a stable organization/device URL. Treat that token as a secret, and do not log or commit it.

NiceGUI On Air is a tech preview, not a substitute for selecting an authentication, authorization, availability, and data-governance model. Before exposing an application, review what data and actions become reachable, add application authentication where needed, and verify the service's current operational and privacy terms. Use ordinary hosted deployment when the application requires controlled networking, durable availability, or organization-owned TLS and access policy.

Deployment Verification

Validate the built deployment through its real entry point and public boundary:

  • process starts with reload disabled and shuts down cleanly under the service manager or container runtime
  • health route, root page, static assets, Socket.IO connection, and reconnect flow work through the proxy or published port
  • app.urls is consumed only after server binding and is not mistaken for canonical proxy configuration
  • storage survives and isolates users, tabs, workers, and executable instances as designed
  • TLS, forwarded headers, cookie flags, upload limits, cache policy, and logs match the public deployment
  • a native build opens, handles window events, closes cleanly, and can run alongside another instance when supported
  • packaged artifacts include NiceGUI and optional-library data files and are tested on each target platform
  • normal background tasks cancel on shutdown, while only explicitly bounded finalization work uses @background_tasks.await_on_shutdown; see interaction mechanics

Sources