- name
- serverbox-webapp
- description
- Adapt a self-hosted web service (Immich, Jellyfin, Paperless-ngx, Vaultwarden, …) into a ServerBox Monitor web app — a folder in `store/apps/<publisher>.<name>/` with a `manifest.toml` and the Docker Compose files the Monitor deploys — then validate it and propose it for the catalog. Use this whenever someone wants to add, port, update, fix or validate an app for the Monitor's app catalog, including requests that never say "manifest", such as "add Jellyfin to the monitor", "make the monitor able to install Nextcloud", "bump Immich to the new release", "why does my web app fail validation", or "turn this docker-compose.yml into a monitor app".
# ServerBox Monitor web apps
A web app is a web service the Monitor deploys with Docker Compose and
the panel's desk shows in a window. Its source is one folder:
```
store/apps/<publisher>.<name>/
manifest.toml what it is, its licence, and the env file it needs
compose.yaml the compose files the manifest names
… any other file they use (extends)
```
The Monitor copies the folder (minus the manifest) into the stack's directory,
writes `.env` from the manifest and the deploy dialog's answers, and runs
`docker compose -p sbm-<publisher>-<name> --env-file .env -f …`. The window reaches the
service on loopback at the port setting `[web] port` names.
`store/apps/futo.immich` is the worked example: Immich's release compose file,
adjusted as below, with GPU acceleration choices.
## Workflow
### 1. Start from upstream's compose file
Take the compose file the project publishes **for the release you pin**
(release assets beat `main`). Write down its version, licence and homepage.
If the licence is copyleft or has terms worth reading, set
`[license] accept = true` so the admin accepts it before deploying.
Copying upstream files is fine when their licence allows it (most do); keep a
header comment saying where each file came from, which release, and what was
changed. Files copied unchanged say so.
### 2. Adjust the compose file
- Remove `name:` and every `container_name:` — the Monitor names the project,
and fixed names collide with a copy the user already runs.
- Pin images to the release (`image: org/app:${APP_VERSION}` with
`APP_VERSION` in `[env]`), or by digest for supporting images.
- Prefer upstream's image. Build one (`build:`, see "Building an image" in
`references/manifest.md`) only when none is maintained.
- Turn every value the user should choose into a `${VARIABLE}`: data paths,
the published port, hardware acceleration. Publish the web port as
`'${BIND}:${PORT}:<container port>'` so it can stay on loopback.
- Keep upstream's structure otherwise (`extends`, healthchecks,
`env_file: [.env]`): a later release is then a small diff.
- Run as the shared folder's owner (`PUID=${SBM_UID}`, `PGID=${SBM_GID}`,
or `user: "${SBM_UID}:${SBM_GID}"`) and set `TZ: ${SBM_TZ}`; the Monitor
writes those three for every app.
- Downloads, media, music, books, comics and documents go in the shared
folder (`{shared}/downloads`, `{shared}/media/movies`, …), not the stack's
own: another app reads them. Ports other machines need (DNS, BitTorrent,
sync) are their own `port` settings with `protocol`.
### 3. Write the manifest
Copy `assets/manifest.template.toml` to `store/apps/<publisher>.<name>/manifest.toml`. Its
first line is a schema directive, so editors with Taplo/Even Better TOML/Tombi
check it as you type. Every field is in `references/manifest.md`.
- One `[[settings]]` per thing the deploy dialog should ask, each writing env
values: `path` (data directories, `default = "{stack}/…"`, `data = true` for
data), `port`, `bool` (`on`/`off` values), `choice` (options with env values
and `suggest` for GPUs), `text` (with a `pattern`).
- `[[secrets]]` for passwords the service needs: generated once, never shown.
- `[login]` for the first sign-in: seed the admin from a secret where the
service allows it, and name it there; the admin can then read it in the
app's details.
- `[env]` for fixed values.
- `[[mcp]]` when the service has an MCP server, or a maintained one exists to
run beside it as a sidecar: Agent mode connects to it once deployed.
- Every env name comes from exactly one of those, and every `${VARIABLE}` a
compose file needs must be one of them.
- `notices`: what the compose files do that an admin should know (bind
mounts, devices, published ports, `privileged`, host network, the Docker
socket). They are shown, never refused — say what is there.
- `title` names the window: the upstream project's own name ("Immich"), `en`
only, never a generic word ("Photos"). Don't use upstream logos; `glyph`
is a Material Symbols name.
### 4. Validate
```sh
python3 <skill>/scripts/validate.py store/apps/<publisher>.<name> --compose
```
It checks the manifest against the published JSON Schema (via
`uvx`/`pipx` `check-jsonschema`; pass `--schema docs/schemas/monitor-app.schema.json`
inside the repository to use the local copy), then the rules only the Monitor
knows (files, env names, defaults, compose variables), and with `--compose`
runs `docker compose config` over the stack with the defaults' env file.
Fix every error; read the warnings. Inside the repository also run:
```sh
cargo test -p server_box_monitor --lib stacks::
```
which reads every folder in `store/apps/` with the Monitor's own reader.
Then deploy it on a machine with Docker through the Monitor, as a desk window
reaches it (HTTPS, through the Monitor's proxy, signed in with what `[login]`
shows):
```sh
SBM_E2E_DOCKER=1 SBM_E2E_CATALOG_APPS=<publisher>.<name> cargo test -p server_box_monitor --test stacks_catalog_e2e -- --ignored --nocapture
```
and add the app's sign-in to that test's `sign_in`.
### 5. Try it
The quickest way is a Monitor itself: put the folder in a directory
the Monitor's `config.toml` names as `[stacks] local_apps` and restart it. The
app shows in the App Store marked Local, and installs, opens, takes new
settings and uninstalls as a catalog app does. `monitor/tests/apps/` holds
a small one (`serverbox.testweb`) the Monitor's Docker e2e test deploys.
Without a Monitor, on a Linux machine with Docker: copy the folder (without the manifest),
write a `.env` like the validator's (`--compose` builds one from the
defaults), `docker compose -p sbm-<publisher>-<name> --env-file .env -f compose.yaml up -d`,
and open the port. Check first-run steps (an admin account to create, a
setup wizard) and mention them in `description` if the window would
otherwise look broken. `docker compose … down` when done.
### 6. Propose it
Open a pull request against `lollipopkit/flutter_server_box` adding the
folder. Say in the description which release it pins, where each file came
from, and that you deployed it (step 5). An update to a new upstream release
changes `version`, the pinned images and whatever the release's compose file
changed.
GitHubで見る