# How to Share a Voilà Dashboard — Hosting a Kernel, or Publishing the Output

Canonical: https://commareports.com/share-voila-notebook
Published: 2026-09-04

> Voilà turns a notebook into a dashboard by keeping a Jupyter kernel running per viewer, which is why there is no static export. Hosting options, the ipywidgets limitation, and the nbconvert route when readers only need the results.

# How to share a Voilà dashboard

Voilà is a clean idea: take a notebook, hide the code cells, and serve the
outputs and widgets as a dashboard. The thing to understand before you plan
any sharing around it is *how* it does that, because the mechanism sets every
constraint that follows.

## Voilà runs a kernel per viewer

When someone opens your Voilà dashboard, the server starts a Jupyter kernel,
executes the notebook, and streams widget state to that browser over a
websocket. Move a slider, the kernel re-runs the callback, the output updates.

Two consequences, both load-bearing:

**There is no static export.** The interactivity *is* the kernel. Ask for a
self-contained file and you are asking for nbconvert, which is a different
tool doing a different job.

**Concurrency costs memory, not bandwidth.** Ten simultaneous readers means
ten kernels. If the notebook loads a 2 GB DataFrame at the top, that is 20 GB.
Most Voilà deployments discover their real capacity limit this way, usually
during a demo.

## Hosting it properly

If the widgets are genuinely the point — a parameter explorer, a simulation
people drive — then host it and accept the shape:

- A container running `voila` behind a reverse proxy with authentication, on
  whatever you already operate.
- **JupyterHub**, if you have it. It already manages per-user kernels, which
  is exactly the problem Voilà creates, and it already has auth.
- **Binder**, for public demos with no data sensitivity, at the cost of a slow
  cold start and no guarantees.

Size the instance by peak concurrent readers, not by average, and load data
lazily inside callbacks rather than at import time so an idle viewer is cheap.

## The far more common case

Somebody wants to see the analysis. They will not move the slider. They want
to read the conclusion, look at two charts, and ask about one of them.

Hosting a kernel farm for that reader is a poor trade, and there is a
one-command alternative.

## Execute and publish

```bash
jupyter nbconvert --to html --execute --no-input \
  --HTMLExporter.theme=light analysis.ipynb --output report.html
```

`--execute` runs it fresh so the outputs match the current data, `--no-input`
hides the code cells the way Voilà does, and the result is one file.

The important nuance: **client-side interactivity survives this.** Plotly,
Bokeh, Altair and folium render as JavaScript that runs in the reader's
browser with no kernel behind it, so zoom, hover and legend toggles all keep
working in the static file. What you lose is precisely the ipywidgets
controls — sliders, dropdowns, buttons — that need Python to respond. If your
dashboard's interactivity is mostly charts rather than widgets, the static
render loses almost nothing.

Then publish, and keep publishing to one id:

```bash
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html report.html \
        --arg title "Cohort analysis — $(date +%F)" '{title: $title, html: $html}')"
```

A [routine](/features/routines) runs the execute-and-publish loop on a
schedule, which covers the "the dashboard should be current" requirement that
usually motivates hosting Voilà in the first place.

## What the published version has that the hosted one does not

**A record.** Voilà always shows now. Every previous state is gone. Published
revisions keep every run, so "what did this look like before the pipeline
change" is a diff.

**Somewhere to put the discussion.** Anchored threads land on a specific
figure — "this cohort excludes trials, that's why it looks flat" — and stay
attached as revisions accumulate.

**No uptime.** No kernel, no memory ceiling, no cold start, no reader waiting
on a container.

Access is a setting on the report: [private, team, domain-gated or
link](/docs/sharing).

## Choosing

Widgets that people actually drive → host Voilà, size for concurrency, put
auth in front. Charts and narrative that people read → execute, publish, and
put the kernel budget somewhere it earns more.

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision
history.

**[Create your first report →](https://commareports.com/)**

### Related

- [Share a Jupyter notebook as HTML](/share-jupyter-notebook-html)
- [Share an nbconvert HTML file](/share-nbconvert-html)
- [Share a Panel app](/share-panel-app)
