shinyhub

module
v0.10.15 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: MIT

README

ShinyHub

PyPI CI

Self-hosted platform for deploying and operating R and Python Shiny applications. Push an app from the CLI, get a clean URL behind a built-in reverse proxy, sign users in with OAuth or OIDC, and let idle apps hibernate and wake on demand. ShinyHub runs as a single Go binary backed by SQLite, with no external services to operate.

ShinyHub dashboard showing the app grid with running apps and per-app CPU and memory

Contents

Features

  • Deploy from the CLI. shinyhub deploy uploads a bundle and brings the app up.
  • Reverse proxy. One URL per app under /app/<slug>/, sticky-session aware.
  • Hibernation. Idle apps are stopped automatically and restarted on the next request.
  • Authentication. Username/password, GitHub OAuth, Google OAuth, or generic OIDC (Okta, Azure AD, Keycloak, Auth0).
  • Access control. Private (members only), shared (every signed-in user), or public apps, with per-app member and IdP-group roles.
  • Env vars and secrets. Per-app key-value store; secrets encrypted at rest with AES-256-GCM. See docs/environment.md.
  • Persistent data dir. Each app gets a data/ directory that survives deploys, with shinyhub data push|ls|rm and a UI tab. See docs/data.md.
  • Scheduled jobs and shared data. Per-app cron schedules and read-only cross-app data mounts for fetcher to consumer pipelines. See docs/schedules.md.
  • Horizontal scaling. Set replicas: N to run multiple load-balanced backends per app, recovered independently on crash. See docs/scaling.md.
  • Fleet reconcile. Declare a whole set of apps in one fleet.toml and converge the server to match, kubectl-apply style. See docs/fleet.md.
  • Observability. OpenTelemetry tracing (proxy, app, and control-plane spans), an opt-in Prometheus /metrics endpoint, and a structured access log with request-ID and trace correlation. See docs/tracing.md and docs/metrics.md.
  • Container isolation (optional). Run each app inside a Docker container with CPU and memory limits.
  • Worker isolation. Per-app session isolation dial: multiplex (shared event loop), grouped (N clients per worker), or per_session (one process per browser client, HOL-free). See docs/isolation.md.
  • Branding (white-label). Customize the front door (title, logo, theme, landing page) without forking. See docs/branding.md.
  • Audit log. Mutating actions recorded for admin review.
  • Single binary, SQLite, no external dependencies.

Quick start

The simplest way to run ShinyHub end to end is with uv: it installs and runs the server in one command, and it is also the runtime ShinyHub uses to launch Python apps, so once uv is on the host you have everything you need. For an isolated or production deployment use Docker; for a standalone server binary see Binary.

uv tool install shinyhub

SHINYHUB_AUTH_SECRET="$(openssl rand -hex 32)" \
  SHINYHUB_ADMIN_USER=admin SHINYHUB_ADMIN_PASSWORD=change-me \
  shinyhub serve

SHINYHUB_AUTH_SECRET (a random 32+ character string) is the only required setting; no YAML file is needed for a basic run. Generate it once and reuse the same value on every restart: it signs sessions and derives the key that encrypts app secrets, so changing it makes existing encrypted data unreadable. Beyond a quick trial, load it from a file or secrets manager (or set auth.secret in shinyhub.yaml). The database, bundles, and per-app data land under ./data/ by default. Open http://localhost:8080 and log in as admin.

Then deploy an app (an app.py + requirements.txt, or an R app.R) from another terminal:

shinyhub login --host http://localhost:8080 --username admin
shinyhub deploy ./my-app --slug demo --wait   # live at /app/demo/

Tip: run the bundle locally first with shinyhub run ./my-app (add --check for a dependency preflight) to catch missing packages before a multi-minute deploy.

uvx shinyhub <cmd> runs any subcommand one-shot, without installing first. pip install shinyhub installs the server too, but native Python apps launch via uv run, so you still need uv on the host to deploy them.

Docker

The published image runs the ShinyHub control plane, proxy, and dashboard, but it is distroless and does not bundle a Python or R runtime, so it cannot run apps on its own. To run apps under Docker, use the deploy/docker-compose stack, which wires up the Docker app runtime (each app in its own container) together with the host networking and path-parity data root that runtime requires. To run apps without containers, run the server on a host that has uv (the uv path above).

To start just the control plane (dashboard + API, not app execution) with a persistent /data volume:

mkdir -p ./data
secret="$(openssl rand -hex 32)"   # generate once; reuse the SAME value on restart

docker run -d \
  --name shinyhub \
  -p 8080:8080 \
  -v "$PWD/data:/data" \
  -e SHINYHUB_AUTH_SECRET="$secret" \
  -e SHINYHUB_ADMIN_USER=admin \
  -e SHINYHUB_ADMIN_PASSWORD=change-me \
  ghcr.io/rvben/shinyhub:latest

The default ./data/... paths resolve inside the mounted /data volume. Open http://localhost:8080 and log in with the admin credentials you set. To deploy apps from here, switch to the deploy/docker-compose stack; for storage and SSO settings, mount a shinyhub.yaml (see Configuration).

Binary

For a host without Python, install the standalone server binary:

curl -fsSL https://raw.githubusercontent.com/rvben/shinyhub/main/scripts/install.sh | sh
# Or download from https://github.com/rvben/shinyhub/releases

SHINYHUB_AUTH_SECRET="$(openssl rand -hex 32)" \
  SHINYHUB_ADMIN_USER=admin SHINYHUB_ADMIN_PASSWORD=change-me \
  shinyhub serve

The binary is the server only. Python apps are launched with uv, so install uv on the host too (or run apps under the Docker runtime).

From source
git clone https://github.com/rvben/shinyhub.git
cd shinyhub
go build -o bin/shinyhub ./cmd/shinyhub

Configuration

Every key is documented inline in shinyhub.yaml.example. Environment variables (prefixed SHINYHUB_) override the YAML.

The server resolves its config file in this order: the --config flag (shinyhub serve --config /path/to/shinyhub.yaml, also honored by backup and restore), then the SHINYHUB_CONFIG environment variable, then ./shinyhub.yaml.

SHINYHUB_CONFIG has two distinct roles. On the SERVER it selects the shinyhub.yaml config file. On CLIENT commands (deploy, env, apps, ...) it selects the credentials file (~/.config/shinyhub/config.json by default, written by shinyhub login). The --config flag on client commands likewise points to the credentials file, not the server YAML. For CI pipelines the simpler approach is to skip the credentials file entirely and supply SHINYHUB_HOST and SHINYHUB_TOKEN directly.

The one required setting is auth.secret: a random 32+ character string (openssl rand -hex 32). The server refuses to start with the placeholder value.

Guides

Guide Topic
Environment and secrets Per-app env vars, encrypted secrets, what apps and builds inherit from the server environment, and private package indexes.
Persistent data dir Pushing data, the app-visible path, authorization, quota, and concurrency.
Scheduled jobs and shared data Per-app cron schedules and read-only cross-app data mounts.
Horizontal scaling Per-app replicas, load balancing, and session admission.
Worker isolation Session isolation dial: multiplex, grouped, and per_session modes.
App startup performance Why data lags the page shell, and the startup-scope and caching patterns that fix it.
Fleet reconcile Declaring and converging a whole set of apps from one file.
Deploy manifest The shinyhub.toml bundle manifest.
Tracing OpenTelemetry propagation, app spans, and control-plane spans.
Metrics and logs The /metrics endpoint, exposed series, and the structured access log.
Branding White-label title, logo, theme, landing page, and footer links.
Native OIDC login (SSO) Terminate OpenID Connect SSO in ShinyHub itself - config, claim mapping, group-to-role, sessions/logout, behind-a-proxy, and HA - no external auth proxy.
Identity forwarding to apps The trusted X-Shinyhub-* headers and signed token apps read to know the connected user, with one-call Python/R helpers.
Reverse-proxy auth - Caddy Authenticate users via Caddy forward_auth and forward the identity to ShinyHub.
Reverse-proxy auth - nginx Authenticate users via nginx auth_request and forward the identity to ShinyHub.
CLI/CI behind an auth proxy Deploy and manage apps from the CLI or CI when ShinyHub is behind an auth proxy that blocks non-browser clients.
OIDC bridge for LDAP/SAML Wrap an LDAP or SAML source with an OIDC bridge (Authelia, Authentik, Keycloak) and use ShinyHub's built-in OIDC login.

Architecture

┌────────────┐    HTTPS    ┌──────────────────┐
│  Browser   │────────────▶│    ShinyHub      │
└────────────┘             │                  │
                           │  ┌────────────┐  │
┌────────────┐    CLI      │  │  API + UI  │  │
│  shinyhub  │────────────▶│  ├────────────┤  │
│  deploy    │             │  │   Proxy    │──┼──▶  app processes
└────────────┘             │  ├────────────┤  │     (native or Docker)
                           │  │   SQLite   │  │
                           │  └────────────┘  │
                           └──────────────────┘

ShinyHub is one Go binary. shinyhub serve runs the HTTP API, the embedded dashboard UI, the reverse proxy, and the lifecycle watchdog against a single SQLite database; the same binary provides the developer subcommands (deploy, login, apps, env, data, and more). App processes run natively or inside Docker containers and are proxied per slug.

Status

Active development. ShinyHub is self-hosted and run in production by the maintainer, offered with no SLA or support guarantees. It runs single-node on SQLite by default; an optional high-availability mode (multiple control-plane instances behind a shared Postgres, with an ownership lease and off-host worker tiers) is also supported - see docs/deployment/ha-data-plane.md. See CHANGELOG.md for the current release.

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md for development setup and conventions.

License

MIT

Directories

Path Synopsis
cmd
shinyhub command
cmd/shinyhub/worker.go
cmd/shinyhub/worker.go
internal
admission
Package admission provides render-aware admission control primitives: a token-bucket pacer, effective-core detection, per-principal fairness, and a host CPU watermark.
Package admission provides render-aware admission control primitives: a token-bucket pacer, effective-core detection, per-principal fairness, and a host CPU watermark.
api
appenv
Package appenv turns an app's stored environment variables into the "KEY=VALUE" slices the runtime injects at start time.
Package appenv turns an app's stored environment variables into the "KEY=VALUE" slices the runtime injects at start time.
autoscale
Package autoscale provides a provider-agnostic replica autoscale controller.
Package autoscale provides a provider-agnostic replica autoscale controller.
autoscalespec
Package autoscalespec holds the pure, I/O-free validation for a declared per-app autoscale policy.
Package autoscalespec holds the pure, I/O-free validation for a declared per-app autoscale policy.
backup
Package backup implements first-class snapshot and restore of all ShinyHub durable state: the database, deployed app bundles, and per-app data dirs.
Package backup implements first-class snapshot and restore of all ShinyHub durable state: the database, deployed app bundles, and per-app data dirs.
bundle
Package bundle defines the single source of truth for which files may appear inside a deploy bundle.
Package bundle defines the single source of truth for which files may appear inside a deploy bundle.
bundletoken
Package bundletoken mints and verifies short-lived capability tokens that authorise a single fetch of one content-addressed bundle.
Package bundletoken mints and verifies short-lived capability tokens that authorise a single fetch of one content-addressed bundle.
cli
data
Package data implements the persistent per-app storage area exposed via `PUT/DELETE/GET /api/apps/:slug/data[/*path]` and the `shinyhub data` CLI.
Package data implements the persistent per-app storage area exposed via `PUT/DELETE/GET /api/apps/:slug/data[/*path]` and the `shinyhub data` CLI.
db
dbtest
Package dbtest provides an env-aware store constructor for tests.
Package dbtest provides an env-aware store constructor for tests.
deployfail
Package deployfail defines the stable vocabulary and classifier for why a deploy attempt failed.
Package deployfail defines the stable vocabulary and classifier for why a deploy attempt failed.
fargate
Package fargate provides a process.Runtime backend that runs each app replica as an AWS ECS task on the Fargate launch type.
Package fargate provides a process.Runtime backend that runs each app replica as an AWS ECS task on the Fargate launch type.
fleet
Package fleet implements the pure, I/O-free core of the fleet reconcile layer: manifest parsing, source-form classification, and the desired-vs- observed diff.
Package fleet implements the pure, I/O-free core of the fleet reconcile layer: manifest parsing, source-form classification, and the desired-vs- observed diff.
history
Package history keeps a short, in-memory time series of per-app resource metrics (CPU, memory, sessions, instance count) for the dashboard's "Trends" view.
Package history keeps a short, in-memory time series of per-app resource metrics (CPU, memory, sessions, instance count) for the dashboard's "Trends" view.
httproute
Package httproute propagates a matched HTTP route pattern through a request context as an immutable string.
Package httproute propagates a matched HTTP route pattern through a request context as an immutable string.
identity
Package identity derives per-app identity keys, sanitizes group lists for header transport, and mints the signed identity token the proxy forwards to app processes.
Package identity derives per-app identity keys, sanitizes group lists for header transport, and mints the signed identity token the proxy forwards to app processes.
leader
Package leader elects a single owner control-plane instance via a fenced, DB-backed lease.
Package leader elects a single owner control-plane instance via a fenced, DB-backed lease.
localrun
Package localrun provides the foreground app runner for `shinyhub run`.
Package localrun provides the foreground app runner for `shinyhub run`.
logging
Package logging wires a process-wide slog.Logger based on env config.
Package logging wires a process-wide slog.Logger based on env config.
metrics
Package metrics exposes Prometheus instrumentation for the ShinyHub server process itself - HTTP request counts/latency for the control-plane API, the Go runtime and process collectors, and build/version + uptime gauges.
Package metrics exposes Prometheus instrumentation for the ShinyHub server process itself - HTTP request counts/latency for the control-plane API, the Go runtime and process collectors, and build/version + uptime gauges.
proxytrust
Package proxytrust centralises the trust gate that controls whether X-Forwarded-* request headers are honoured.
Package proxytrust centralises the trust gate that controls whether X-Forwarded-* request headers are honoured.
sandbox
Package sandbox applies best-effort, unprivileged process isolation to native app processes.
Package sandbox applies best-effort, unprivileged process isolation to native app processes.
schedulespec
Package schedulespec defines schedule validation rules shared between the HTTP API and the deploy-manifest application path.
Package schedulespec defines schedule validation rules shared between the HTTP API and the deploy-manifest application path.
secrets
Package secrets encrypts and decrypts small values (env var secrets) using AES-256-GCM with a key derived from the server's auth secret via HKDF-SHA256.
Package secrets encrypts and decrypts small values (env var secrets) using AES-256-GCM with a key derived from the server's auth secret via HKDF-SHA256.
servertrace
Package servertrace adds optional OpenTelemetry tracing for the ShinyHub server process itself - spans for the control-plane API request handling, exported to the same OTLP endpoint the managed apps use.
Package servertrace adds optional OpenTelemetry tracing for the ShinyHub server process itself - spans for the control-plane API request handling, exported to the same OTLP endpoint the managed apps use.
slug
Package slug holds the canonical slug-validation regex shared by the API server, CLI, and any future surface that needs to validate or display the rules.
Package slug holds the canonical slug-validation regex shared by the API server, CLI, and any future surface that needs to validate or display the rules.
storage
Package storage owns ShinyHub's on-disk and pluggable storage concerns: the on-disk lifecycle helpers shared by the API and CLI (the slug-keyed convention for per-app directories, one under Storage.AppsDir for code and one under Storage.AppDataDir for persistent data), plus the DataVolume abstraction that provisions per-app mutable data on a single host's local disk (or, in a later phase, a shared backend).
Package storage owns ShinyHub's on-disk and pluggable storage concerns: the on-disk lifecycle helpers shared by the API and CLI (the slug-keyed convention for per-app directories, one under Storage.AppsDir for code and one under Storage.AppDataDir for persistent data), plus the DataVolume abstraction that provisions per-app mutable data on a single host's local disk (or, in a later phase, a shared backend).
tracing
Package tracing implements lightweight W3C-trace-context propagation through the reverse proxy and a per-app ring buffer of recent slow/error proxy spans for the UI's Traces tab.
Package tracing implements lightweight W3C-trace-context propagation through the reverse proxy and a per-app ring buffer of recent slow/error proxy spans for the UI's Traces tab.
ui
upgrade
Package upgrade wraps github.com/cloudflare/tableflip behind a small interface so ShinyHub can perform zero-downtime, listener-preserving restarts (SIGHUP handoff) and unit-test the wiring with a fake.
Package upgrade wraps github.com/cloudflare/tableflip behind a small interface so ShinyHub can perform zero-downtime, listener-preserving restarts (SIGHUP handoff) and unit-test the wiring with a fake.
worker
internal/worker/ca.go
internal/worker/ca.go
worker/agent
internal/worker/agent/agent.go
internal/worker/agent/agent.go
worker/api
Package api defines the JSON request/response types and the NDJSON streaming framing that form the wire contract between the ShinyHub control plane and a worker agent.
Package api defines the JSON request/response types and the NDJSON streaming framing that form the wire contract between the ShinyHub control plane and a worker agent.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL