bino serve
bino serve starts a production HTTP server that serves a LiveReportArtefact as an interactive web application.
Unlike preview, serve does not watch for file changes and renders on-demand per request.
Required flags:
--live- name of theLiveReportArtefactto serve (required).
Common flags:
--work-dir- report bundle directory (default:.).--port- HTTP port (default:8080).--addr- full listen address (overrides--port, e.g.0.0.0.0:8080).--log-sql- log executed SQL queries.--data-mode- dataset delivery:inline(default) embeds the data gzip+base64 in the HTML,urlfetches it over HTTP from the serve process.
With url, the browser loads each DataSet and DataSource with its own request to /__bino/data/. The response is sent with Cache-Control: private, no-store. The data is available while its page is among the 100 most recently used pages of the serve process. Behind several replicas the request must reach the process that rendered the page. The data URL does not carry the query parameters of the route, only a hash of the content. So a proxy that sets a parameter for the viewer does not restrict it: whoever knows the URL of a body gets that body. inline has no such limits, but the page is larger.
How it works
Section titled “How it works”- Loads configuration - reads all manifests from the work directory.
- Validates routes - ensures the selected
LiveReportArtefacthas a root route and all referencedReportArtefacts exist. - Serves on-demand - renders reports when requested, using caches for efficiency.
- Nvigation - enables smooth client-side navigation without full page reloads.
Data sent to the browser
Section titled “Data sent to the browser”A served page carries the rows of the DataSets and DataSources that its rendered components bind with dataset. A DataSource is bound as $name. This includes components pulled in with ref and components inside cards, grids and tree nodes. It is the same for both --data-mode values. A route with several LayoutPages carries the data of all of them. Other DataSets and DataSources of the project are not sent to that page.
The value and label columns of a DataSet used for the options of a select parameter are also sent to the page.
A component filter runs in the viewer’s browser. Every row of the component’s dataset reaches the viewer. The filter only decides which rows are drawn.
To restrict the rows a page carries, filter in the DataSet SQL with the route’s query parameter, and bind the component to that DataSet:
With ?REGION=EU, the page carries only the EU rows of sales. The LayoutPage below draws the same rows, but its Table binds $sales directly, so every row of sales reaches the browser:
Filtering in the DataSet limits the rows per viewer only when a layer in front of bino serve, for example a proxy, sets the parameter and the viewer cannot change it.
See Query parameter substitution for how parameters are substituted.
Query parameters
Section titled “Query parameters”Each route in a LiveReportArtefact can define its own query parameters that are substituted into report documents using ${VAR} syntax. This allows dynamic filtering and customization of reports on a per-route basis.
Query parameters support different input types in the serve UI sidebar:
string(default) - text inputnumber- numeric input with optional min/max/stepnumber_range- dual range slider for range filteringselect- dropdown with static items or options from a DataSetdate- date pickerdate_time- date and time picker
Parameters are required by default. To make a parameter optional, either:
- Set a
defaultvalue, or - Set
optional: true(the parameter will be empty if not provided)
If a required parameter is missing, the server returns HTTP 400 with a JSON error listing the missing parameters.
Access with: http://localhost:8080/region?REGION=EU&YEAR=2024
See LiveReportArtefact reference for full documentation of parameter types and options.
Default Arguments via bino.toml
Section titled “Default Arguments via bino.toml”Set default values for serve flags in your project’s bino.toml:
Environment Variables via bino.toml
Section titled “Environment Variables via bino.toml”Set environment variables for serve in bino.toml:
Examples
Section titled “Examples”Serve a live report artefact:
Serve on a specific port with SQL logging:
Serve bound to all interfaces (for Docker/Kubernetes):
The default bind is 127.0.0.1, which is unreachable from outside a container, so --addr is
mandatory there. It is also the only serve flag that cannot be set from bino.toml - pass it on the
command line.
See Recipe: Deploy a live dashboard for Compose and Kubernetes manifests, and Running bino in Docker for the image itself.
Progressive Web App
Section titled “Progressive Web App”When the served LiveReportArtefact defines spec.pwa, bino serve makes the application installable as a Progressive Web App:
GET /manifest.webmanifest- the web app manifest, generated fromspec.pwa(names, colors, display mode, icons). Titled non-root routes additionally become manifestshortcuts- the jump list shown when long-pressing or right-clicking the installed app’s icon.GET /sw.js- the service worker.- Icon files come from the referenced
Assetmanifests and are served atassets/files/<name>. - Every page gets the manifest link and the service-worker registration injected into its
<head>- plus a theme-color meta tag whenthemeColoris set, and an apple-touch-icon link when at least one icon has purposeany.
The two paths are reserved: a route at /manifest.webmanifest or /sw.js fails validation, even when spec.pwa is not set. Without spec.pwa, serving is unchanged - no extra tags or routes.
The injected tags use relative URLs. When accessing bino serve directly, install from a single-segment route such as / or /sales: on a nested route like /reports/q1 the browser resolves the relative links into the route’s subdirectory, where they are not found. Behind a reverse proxy that injects a <base> tag (as bino cloud does) the links resolve on every route.
Install requirements
Section titled “Install requirements”Browsers only offer installation on a secure context: HTTPS or localhost. http://localhost:8080 is enough to try it out during development; a deployed dashboard must sit behind TLS - see Recipe: Deploy a live dashboard. All generated URLs are relative, so the app is installable whether it is served at the domain root or behind a path prefix.
Offline behavior
Section titled “Offline behavior”The service worker caches the static engine assets (scripts, styles, fonts) so an installed app starts quickly. Report data is never cached: rendering a route always requires connectivity to the serve process. When the network is unreachable, the app shows an offline fallback page instead of stale data.
Service worker updates
Section titled “Service worker updates”The browser re-fetches sw.js in the background when the app is opened. A changed worker installs, activates immediately, and takes control of the running app - no relaunch needed - dropping caches from older engine versions, so updated engine assets roll out without user action.
Health check
Section titled “Health check”GET /healthz returns 200 with the body ok and requires no authentication. It reports that the
process is up and serving HTTP; it does not verify database connectivity or that any route renders.
Use it for container health checks and Kubernetes probes.
Comparison with preview
Section titled “Comparison with preview”| Feature | bino preview |
bino serve |
|---|---|---|
| File watching | ✓ Yes | ✗ No |
| Hot reload | ✓ Yes | ✗ No |
| Query param substitution | ✗ No | ✓ Yes |
| Production-ready | ✗ No | ✓ Yes |
| Navigation | ✗ No | ✓ Yes |
| Default port | 45678 | 8080 |
Use preview during development for fast iteration. Use serve for production deployments.