Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Where Ruscker fits

Ruscker is a portal-and-orchestrator for container-per-session and container-per-API workloads — interactive apps that want a fresh container per visitor, and stateless HTTP services pooled per replica. If your apps run in a container and speak HTTP or WebSocket, Ruscker wraps them in a portal, a reverse proxy, sticky sessions, auto-scaling and an admin panel — configured by a single YAML file.

What Ruscker is good at

  • Per-session isolation — one container per visitor for stateful apps (Shiny, Streamlit, Dash, Voilà, notebooks), so sessions never share state.
  • Stateless APIs — pooled per replica with an auto-scaler (Plumber2, FastAPI, and any HTTP service).
  • Mixed frameworks under one portal — every app is a card on the landing page; anything in a container is first-class.
  • A real admin panel — apps CRUD, a media library, an encrypted credentials store, a live monitoring dashboard, an audit log, and user roles — instead of editing a file and restarting.
  • Light to run — a single static binary with a low-tens-of-MB idle footprint and instant startup.

Self-hosting a single framework

Streamlit, Dash, Voilà and Gradio ship their own dev server but no multi-app portal, session isolation, auth, or scaling — self-hosting means rolling your own reverse proxy, container lifecycle and landing page. That glue is exactly what Ruscker is. Point a spec at your image and you get the portal, sticky sessions, WebSocket forwarding, scaling and reaping for free. See What Ruscker can serve.

Sub-path handling (the strip model)

Ruscker uses a strip model: the proxy strips /app/{id} from the request path before forwarding, so the container always receives a root-relative path (e.g. /lab/... instead of /app/jupyter/lab/...). The container does not need to know its mount path — the proxy injects a <base href>, rewrites static URLs in HTML responses, and patches runtime fetches via a small JS shim.

The practical consequence: serve apps at the root and don’t hard-code a mount path. Jupyter, for example, runs at --ServerApp.base_url=/:

- id: jupyter
  container-image: quay.io/jupyter/minimal-notebook:latest
  container-port: 8888
  container-cmd:
    - start-notebook.py
    - --IdentityProvider.token=
    - --ServerApp.allow_origin=*
    - --ServerApp.base_url=/

If you’re carrying over a config where the app reads a *_PUBLIC_PATH environment variable to self-prefix its URLs, drop it under Ruscker — the strip model already handles the mount path, and a self-prefixing app would misconfigure itself and 404 on every request.

For the rare app that genuinely needs its external mount path — to build absolute URLs the proxy can’t rewrite — Ruscker exposes an explicit opt-in token #{publicPath}, substituted at spawn with the spec’s actual mount path (including any --base-path prefix), for use in container-cmd or container-env. Do not use it for Jupyter: under the strip model a non-root base_url makes it 404 every path (see Troubleshooting). Apps like Shiny, Streamlit, Dash and Voilà never need it.

Secrets via env-var interpolation

Ruscker supports ${VAR} references in the YAML, resolved at spawn, not at parse: the literal ${DB_PASSWORD} is stored in the config and the database; only when a container is actually started does Ruscker look up the environment variable and inject the real value. This means secrets never land in the database — the DB only ever sees the placeholder. Set secrets in the process environment (or in /etc/ruscker/ruscker.env for the systemd service) and reference them by name in the YAML.

When Ruscker is not the right tool

  • You need a publishing / authoring workflow — pushing content from an IDE, scheduled reports, content versioning. Ruscker is a proxy/orchestrator; you bring your own container images.
  • You’re all-in on Kubernetes and want a CRD-native operator today — Ruscker schedules onto Docker hosts (over ssh/tcp), not Kubernetes.
  • You need enterprise SSO gating app access per user (OIDC/SAML/LDAP at the proxy level) — Ruscker’s auth covers admin roles (Viewer / Editor / Admin) and per-app visibility (access-groups / access-users), but proxy-level SSO is not yet supported.

If none of those apply, start with the Quickstart.